题意: n个基站,架设第i个基站花费为p[i]. m个加成,第j个加成为(a[j],b[j],c[j]) 表示 若架设基站(a[j],b[j]) 则带来收益c[j].
n<=5e3, m<=5e4. 0<=c[i],p[i]<=100. 可以选任意个基站架设,问最大收益为多少?
建图:n+m个顶点. [1..n]顶点的权值为-p[i]. [n+1,n+m]个顶点的权值为c[j].
选中第j个顶点表示存在c[j]加成, 那么(a[j],b[j])这两个顶点(基站)必须要选. 连接边(j,a[j]) ,(j,b[j])
现在问题转化问 在图中选出一个点集V, 对任意的(u,v)属于E,若u属于V,则v必须也属于V.并且点集权值最大.
则相当于求一个最大权闭合图.
建模: 源点s向正权点(加成点)连接容量为c[j]的边. 负权点(基站点)向汇点t连接容量为p[i]的边.
并且若(u,v)属于E, 则(u,v)边的容量为oo. (保证了最小割为简单割)
建模后:闭合图对应一个简单割,简单割对应一个闭合图 即 V1 U {s}=S
V1为某个闭合图,可以证明w(V1)=Σw^(+)[v] - C[S,T] 求出新图的最小割即可.
ps:跑二分图时Dinic复杂度为O(sqrt(n) *m)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e5+5,M=2e5+5,inf=0x3f3f3f3f;
struct edge{
int to,cap,nxt;
edge(int to=0,int cap=0,int nxt=0):to(to),cap(cap),nxt(nxt){}
}e[M*2];
int n,m,tot,head[N],s,t,dis[N];
int p[N],u,v,w;
void init(){
tot=0;
s=0,t=n+m+1;
memset(head,-1,sizeof(head));
}
void add_edge(int u,int v,int cap){
e[tot]=edge(v,cap,head[u]);
head[u]=tot++;
e[tot]=edge(u,0,head[v]);
head[v]=tot++;
}
int dfs(int u,int x){
if(u==t||x==0) return x;
int dx,res=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].to;
if(dis[v]!=dis[u]+1) continue;
if(dx=dfs(v,min(x,e[i].cap))){
e[i].cap-=dx;
e[i^1].cap+=dx;
x-=dx,res+=dx;
if(x==0) return res;//go back
}
}
dis[u]=-1;//del u
return res;
}
bool bfs(){
queue<int> q;
memset(dis,-1,sizeof(dis));
dis[s]=0,q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].to;
if(dis[v]==-1&&e[i].cap){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[t]!=-1;
}
int Dinic(){
int res=0;
while(bfs())res+=dfs(s,inf);
return res;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>m;
init();
for(int i=1;i<=n;i++) cin>>p[i];
int res=0;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
if(w>0) res+=w;
add_edge(i+n,u,inf);
add_edge(i+n,v,inf);
add_edge(s,i+n,w);
}
for(int i=1;i<=n;i++) add_edge(i,t,p[i]);
int mx_flow=Dinic();
res-=mx_flow;
cout<<res<<'\n';
return 0;
}