BZOJ1497: [NOI2006]最大获利
【传送门:BZOJ1497】
简要题意:
总公司有n个可以修建的电站(一开始一个电站都没有修建),每个电站都有一个修建的成本p,给出m个客户,每个客户都必定从x电站打电话给y电站,会带给总公司带来c的利益,求出能得到的最大利益
题解:
一开始想到网络流,但是不知道怎么建边,而且网上神犇都用什么“最大权闭合子图”,“最小割”,作为蒟蒻,我都不会QAQ
结果同级的肉丝神犇告诉我,最小割就是最大流,茅“厕”顿开(虽然对我做这道题没什么影响)
其实还是用网络流,将源点连向n个电站,流量为每个电站的成本,对于每个客户所给出的x,y,c,将x电站和y电站都连向客户,流量不限(也就是给无限流量),然后将客户全部连向汇点,流量为c
这样建得到的最大流还不是答案,首先答案是选择的客户的总利润减去修建电站的成本,其实就是所有客户的利润减去未选择的客户的利益,再减去选择修建的电站的成本,而我们上面网络流得出的最大流正是未选择的客户的利益加上选择修建的电站的成本(至于为什么,自己想想QAQ),这样我们用Σc-最大流就是答案了!!
后来才发现,原来这就是最大权闭合子图的思想
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; struct node { int x,y,c,next,other; }a[1100000];int len,last[510000]; int st,ed; void ins(int x,int y,int c) { int k1,k2; k1=++len; a[k1].x=x;a[k1].y=y;a[k1].c=c; a[k1].next=last[x];last[x]=k1; k2=++len; a[k2].x=y;a[k2].y=x;a[k2].c=0; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } int h[510000],list[510000],head,tail; bool bt_h() { memset(h,0,sizeof(h));h[st]=1; head=1;tail=2;list[1]=st; while(head!=tail) { int x=list[head]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&h[y]==0) { h[y]=h[x]+1; list[tail++]=y; } } head++; } if(h[ed]==0) return false; return true; } int findflow(int x,int f) { if(x==ed) return f; int s=0,t; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&h[y]==(h[x]+1)&&f>s) { t=findflow(y,min(f-s,a[k].c)); s+=t; a[k].c-=t;a[a[k].other].c+=t; } } if(s==0) h[x]=0; return s; } int main() { int n,m; scanf("%d%d",&n,&m); st=0;ed=n+m+1; len=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); ins(st,i,x); } int sum=0; for(int i=1;i<=m;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,n+i,999999999); ins(y,n+i,999999999); ins(n+i,ed,c); sum+=c; } int ans=0; while(bt_h()) { ans+=findflow(st,999999999); } printf("%d\n",sum-ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚