题意:n个项目m个问题,完成每个项目有对应收入,解决每个问题需要对应花费,
给出每个项目需解决的问题以及各问题间的依赖关系,求最大利润(可完成部分或全部项目);
思路:网络流中的最大权闭合图的典型应用——利润问题,参考07年day2国家集训队胡伯涛的论文;
参考:http://blog.csdn.net/u012965890/article/details/38761043
闭合图指所有顶点的出边指向的所有顶点均在图中;
最大权闭合图:点权和最大的闭合图,即权值最大的子闭合图;
建图思路:
工程为点权为正的点,问题为点权为负的点;
源点到工程建边,容量为工程对应的利润;
问题到汇点建边,容量为问题花费;
工程到问题建边,容量为正无穷;
问题间建边,容量为正无穷,不用判环;
最终所求利润为工程总利润-最大流;
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define inf 0x7fffffff int n,m,st,ed; int p[500100],q[500100]; int dis[500010],vis[500010]; int head[500010]; int cnt,u,v; struct node { int v,c,next; }; node E[5000100]; void add(int u,int v,int w) //建边 { E[cnt].v=v; E[cnt].c=w; E[cnt].next=head[u]; head[u]=cnt++; E[cnt].v=u; E[cnt].c=0; E[cnt].next=head[v]; head[v]=cnt++; } bool bfs() { memset(dis,0,sizeof(dis)); dis[st]=1; queue<int>Q; Q.push(st); while(!Q.empty()) { int d=Q.front(); Q.pop(); for(int i=head[d];i!=-1;i=E[i].next) { if(E[i].c&&!dis[E[i].v]) { dis[E[i].v]=dis[d]+1; Q.push(E[i].v); } } } return dis[ed]>0; } int dicnic(int low,int p) //增广 { int f=low; if(p==ed) return f; for(int i=head[p];i!=-1;i=E[i].next) { if(E[i].c&&dis[E[i].v]==dis[p]+1) { int a=E[i].c; int t=dicnic(min(a,low),E[i].v); E[i].c-=t; E[i^1].c+=t; low-=t; if(low<=0) break; } } //printf("fjashdfj "); if(f-low<=0) dis[p]=-1; return f-low; } int flow() { int sum=0; while(bfs()) sum+=dicnic(inf,st);//参数位置不能改变 return sum; } int main() { int t,cas,i,j,k,temp; scanf("%d",&t); for(cas=1;cas<=t;cas++) { scanf("%d%d",&n,&m); int sr=0,sc=0; st=0,ed=n+m+1; //设置源点,汇点 cnt=0; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++) { scanf("%d",&p[i]); add(st,i,p[i]); //源点到项目建边 sr+=p[i]; //总利润 } for(i=1;i<=m;i++) { scanf("%d",&q[i]); add(i+n,ed,q[i]); //问题到汇点建边 } for(i=1;i<=n;i++) { scanf("%d",&k); while(k--) { scanf("%d",&temp);temp++; add(i,temp+n,inf); //项目到问题建边 } } for(i=1;i<=m;i++) for(j=1;j<=m;j++) { scanf("%d",&temp); if(temp==1) add(i+n,j+n,inf); //问题建边,容量设为inf,可不用判环 } int ans=sr-flow(); printf("Case #%d: ",cas); printf("%d ",ans); } return 0; }