CF1082G 题解

CF1082G 题解

$\text{Description}$

给定一个 $n$ 点 $m$ 边无向图,边有边权 $b_i$,点有点权 $a_i$,要求选出一个点集,使得其导出子图的边权和减其点权和最大。

$1\le n,m\le 10^3$

$\text{Solution}$

若选择了一条边,则其两个端点也必须被选择,则问题可以转换为最大权闭合图。

考虑建立源点 $S$ 与汇点 $T$,原图中的每个点看作新图中的点,编号 $[1,n]$,原图中的每条边也看作新图中的点,编号 $[n+1,n+m]$。

对于原图中的每个点 $i$,连边 $(i,T,a_i)$。

对于原图中的每条边 $i(u,v)$,连边 $(S,n+i,b_i),(n+i,u,+\infty),(n+i,v,+\infty)$。

答案即边权和减去该流图的最小割。

首先由于原图中边与点对应的点间的边容量为 $+\infty$,故我们不会选择删这些边。

我们把删去原图中的点与汇点的边看作选择该点,那么,如果有一条原图中的边,其两端点有任意一个没有被选择(即与汇点的边没有被删除),那么源点到其在新图中对应的点的边就必须被删除,否则连通。

那么一个可行的割即为所有被选择的点的点权和,加上不在选出的点集的导出子图中的边的边权和。

用总边权和减去该割就是这个点集的权值。

为了让这个权值最大,我们需要让割最小,即最小割。

根据最大流最小割定理,我们只需跑一遍 Dinic 即可。

$\text{Code}$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h>
#define REG register
using namespace std;
inline void read(int& x){
REG char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}
typedef long long ll;
const int N=5005,M=100005;
const ll INF=0x3f3f3f3f3f3f3f3fll;

int n,m,s,t,u,v;

int Nv[N],Mv[M];

struct Edge{int v,nxt,w;}ed[M];
int head[N],cur[N],cnt=1;
inline void adde(int u,int v,int w){ed[++cnt]=(Edge){v,head[u],w},head[u]=cnt;}
inline void add(int u,int v,int w){adde(u,v,w),adde(v,u,0);}

ll Mf;

int dep[N];

queue<int> Q;

bool bfs(){
memset(dep,0,sizeof dep);
while(Q.size()) Q.pop();
Q.push(s),dep[s]=1,cur[s]=head[s];
while(Q.size()){
int now=Q.front();Q.pop();
for(REG int i=head[now];i;i=ed[i].nxt){
int v=ed[i].v,w=ed[i].w;
if((!w)||dep[v]) continue;
dep[v]=dep[now]+1,Q.push(v),cur[v]=head[v];
if(v==t) return 1;
}
}
return 0;
}

ll dfs(int now,ll flow){
if(now==t) return flow;
ll rst=flow;
for(REG int i=cur[now];i&&rst;i=ed[i].nxt){
int v=ed[i].v;
if(dep[v]!=dep[now]+1||!ed[i].w) continue;
cur[now]=i;
ll k=dfs(v,min(1ll*ed[i].w,rst));
if(!k) dep[v]=0;
ed[i].w-=k,ed[i^1].w+=k,rst-=k;
}
return flow-rst;
}

ll ans;

inline void Work(){
read(n),read(m),s=n+m+1,t=s+1;
for(REG int i=1;i<=n;++i) read(Nv[i]),add(i,t,Nv[i]);
for(REG int i=1;i<=m;++i) read(u),read(v),read(Mv[i]),add(n+i,u,INF),add(n+i,v,INF),add(s,n+i,Mv[i]),ans+=Mv[i];
while(bfs())
if(Mf=dfs(s,INF)) ans-=Mf;
printf("%lld\n",ans);
}

int main(){Work();}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×