[bzoj1497][NOI2006]最大获利_网络流_最小割
最大获利 bzoj-1497
题目大意:可以建立一个点,花费一定的代价;将已经建立的两个点之间连边,得到一定收益。有些节点之间是不允许连边的。
注释:1<=点数<=5,000,1<=允许连边的边数<=50,000。
想法:将每个可以相连的点之间的边在网络流里建立成一个节点,将源点连这条边的收获,向这两条边的端点分别连两条inf的边,所有的端点向源点连边权为该点代价的边。然后由最大流等于最小割,求最大流即可。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define inf 0x3f3f3f3f using namespace std; int head[400010],to[400010],nxt[400010],tot=1; int dis[400100]; int n,m; // int p[10010]; int f[400010]; inline void add(int x,int y,int z) { to[++tot]=y; f[tot]=z; nxt[tot]=head[x]; head[x]=tot; } bool bfs() { queue<int> q; memset(dis,-1,sizeof dis); q.push(1);dis[1]=0; while(!q.empty()) { int x=q.front();q.pop(); for(int i=head[x];i;i=nxt[i]) { if(f[i]>0&&dis[to[i]]==-1) { dis[to[i]]=dis[x]+1; q.push(to[i]); if(to[i]==n+m+2) return true; } } } return false; } int dinic(int x,int flow) { int a,temp=flow; if(x==n+m+2) return flow; for(int i=head[x];i;i=nxt[i]) { if(f[i]>0&&dis[to[i]]==dis[x]+1) { a=dinic(to[i],min(f[i],temp)); if(!a) dis[to[i]]=-1; temp-=a; f[i]-=a; f[i^1]+=a; if(!temp) break; } } return flow-temp; } int main() { cin >> n >> m; int all=n+m+2; for(int a,i=1;i<=n;i++) { cin >> a; add(1+m+i,all,a); add(all,1+m+i,0); } int sum=0; for(int a,b,c,i=1;i<=m;i++) { cin >> a >> b >> c; sum+=c; add(1,1+i,c);add(1+i,1,0); add(1+i,1+m+a,inf);add(1+m+a,1+i,0); add(1+i,1+m+b,inf);add(1+m+b,1+i,0); } int ans=0; while(bfs()) { // puts("Fuck"); ans+=dinic(1,inf); } cout << sum - ans << endl; return 0; }
小结:最小割等于最大流
| 欢迎来原网站坐坐! >原文链接<