【BZOJ1497】【NOI2006】—最大获利(网络流/最小割)

传送门

考虑到一个问题:

建立中转站是花钱,而满足用户需求是获利
怎么将这两个东西结合起来?

换个角度:

假设我们能满足所有用户的要求
那如果我们选择不满足客户的需求不就是也在花钱了吗
或者说我们把客户的需求看做要花那么多钱

那我们现在就有两个选择了

要么花钱修中转站来满足客户需求
要么花钱来解决客户的需求

我们现在要做的就是找到一种花费最少的方案
来解决所有客户的需求

如果我们把解决需求看做是断开一条边的话
那我们从源点连向所有的中转站,所有的客户连向汇点,有需求的互相连边,花费为边权

我们就是在求这个图的最小割

又由于放弃客户和建中转站显然是矛盾的

连一条为infinf的边就可以了


UPDATE:UPDATE:现在看起来好像就是最大权闭合子图
原谅那时的我太naiivenaiive不知道这东西

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=150005;
const int M=400005;
const int inf=20030224;
int n,m,cnt=1,adj[N],tp[N],nxt[M],to[M],cap[M],lev[N],str,des,tot;
inline void addedge(int u,int v,int w){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,cap[cnt]=w;
	nxt[++cnt]=adj[v],adj[v]=cnt,to[cnt]=u,cap[cnt]=0;
}
inline bool bfs(){
	memset(lev,-1,sizeof(lev));
	queue<int> q;
	q.push(str),lev[str]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int e=adj[u];e;e=nxt[e]){
			int v=to[e];
			if(cap[e]>0&&lev[v]==-1){
				lev[v]=lev[u]+1,q.push(v);	
				if(v==des)return true;
			}
		}
	}
	return false;
}
int dinic(int u,int flow){
	if(u==des)return flow;
	int res=0;
	for(int &e=tp[u];e;e=nxt[e]){
		int v=to[e];
		if(cap[e]>0&&lev[v]==lev[u]+1){
			int now=dinic(v,min(flow-res,cap[e]));
			cap[e]-=now,cap[e^1]+=now,res+=now;
			if(flow==res)return res;
		}
	}
	return res;
}
inline int solve(){
	int res=0;
	while(bfs()){
		memcpy(tp,adj,sizeof(adj));
		res+=dinic(str,inf);
	}
	return res;
}
int main(){
	n=read(),m=read();str=n+m+1,des=str+1;
	for(int i=1;i<=n;i++){
		int p=read();
		addedge(str,i,p);
	}
	for(int i=1;i<=m;i++){
		int a=read(),b=read(),c=read();
		addedge(a,i+n,inf),addedge(b,i+n,inf);
		addedge(i+n,des,c),tot+=c;
	}
	tot-=solve();
	cout<<tot<<'\n';
}
posted @ 2019-01-26 15:30  Stargazer_cykoi  阅读(108)  评论(0编辑  收藏  举报