HDU 4971 - A simple brute force problem【最大权闭合图】
有n(20)个工程,完成每个工程获得收益是p[i],m(50)个需要解决的难题,解决每个难题花费是c[i]
要完成第i个工程,需要先解决ki个问题,具体哪些问题,输入会给出
每个难题之间可能有依赖关系,比如i->j就是解决问题j需要实现解决问题i。(题目描述有问题,但是按照样例来看,是前后说反了,也就是按照题意这个地方反向建图就可以)
问,最大收益可以是多少
比较裸的最大权闭合图,解决最大权闭合图一般用最大流的方法
然而训练的时候并不知道这个,所以结束后看了看相关的资料,
下面仅说明一下建图方法。
建立一个源点S,s向每个工程点连一条边,权值是p[i],
建立一个汇点T,t向每个问题连一条边,权值是c[i]
接着,工程与完成工程需要的解决的问题,连一条边,权值INF
难题之间按照题目连边,权值仍为INF
求出最大流maxflow
答案就是sigema(p[i])-maxflow
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<stack> #include<vector> #include<queue> #include<string> #include<sstream> #define eps 1e-9 #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define FOR(i,j,k) for(int i=j;i<=k;i++) #define MAXN 1005 #define MAXM 40005 #define INF 0x3fffffff #define PB push_back #define MP make_pair #define X first #define Y second #define lc (k<<1) #define rc ((k<<1)1) #define V(x) vector<x > #define vs V(string) #define vi V(int) #define fr(x,y,z) for ((x)=(y);(x)<(z);(x)++) #define fo(x,y) fr(x,0,y) #define fir(n) fo(i,n) #define fjr(n) fo(j,n) #define fkr(n) fo(k,n) #define fi fir(n) #define fj fjr(n) #define fk fkr(n) #define pb push_back #define sz size() #define cs c_str() #define clr(x,y) memset((x),(y),sizeof(x)) #define df double using namespace std; typedef long long LL; int i,j,k,n,m,x,y,T,ans,big,cas,num,len; bool flag; const int inf = 0x3f3f3f3f; struct edgenode { int from,to,next; int cap; }edge[MAXM]; int Edge,head[MAXN],ps[MAXN],dep[MAXN]; void add_edge(int x,int y,int c) { edge[Edge].from=x; edge[Edge].to=y; edge[Edge].cap=c; edge[Edge].next=head[x]; head[x]=Edge++; edge[Edge].from=y; edge[Edge].to=x; edge[Edge].cap=0; edge[Edge].next=head[y]; head[y]=Edge++; } int dinic(int n,int s,int t) { int tr,flow=0; int i,j,k,l,r,top; while(1){ memset(dep,-1,(n+1)*sizeof(int)); for(l=dep[ps[0]=s]=0,r=1;l!=r;)//BFS部分,将给定图分层 { for(i=ps[l++],j=head[i];j!=-1;j=edge[j].next) { if (edge[j].cap&&-1==dep[k=edge[j].to]) { dep[k]=dep[i]+1;ps[r++]=k; if(k==t) { l=r; break; } } } } if(dep[t]==-1)break; for(i=s,top=0;;)//DFS部分 { if(i==t)//当前点就是汇点时 { for(k=0,tr=inf;k<top;++k) if(edge[ps[k]].cap<tr)tr=edge[ps[l=k]].cap; for(k=0;k<top;++k) edge[ps[k]].cap-=tr,edge[ps[k]^1].cap+=tr; flow+=tr; i=edge[ps[top=l]].from; } for(j=head[i];j!=-1;j=edge[j].next)//找当前点所指向的点 if(edge[j].cap&&dep[i]+1==dep[edge[j].to]) break; if(j!=-1) { ps[top++]=j;//当前点有所指向的点,把这个点加入栈中 i=edge[j].to; } else { if (!top) break;//当前点没有指向的点,回溯 dep[i]=-1; i=edge[ps[--top]].from; } } } return flow; } int p[MAXN],c[MAXN],t; int main() { scanf("%d",&T); while (T--) { memset(head,-1,sizeof(head)); Edge=0; scanf("%d%d",&n,&m); int sum=0; for (i=0;i<n;i++) { scanf("%d",&p[i]); add_edge(0,i+1,p[i]); sum+=p[i]; } for (i=0;i<m;i++) { scanf("%d",&c[i]); add_edge(n+1+i,n+m+1,c[i]); } for (i=0;i<n;i++) { scanf("%d",&k); //E[i].clear(); for (j=0;j<k;j++) { scanf("%d",&t); add_edge(i+1,t+n+1,INF); //E[i].PB(t); } } for (i=0;i<m;i++) { for (j=0;j<m;j++) { scanf("%d",&t); if (t) { add_edge(i+1+n,j+1+n,INF); } } } printf("Case #%d: %d\n",++cas,sum-dinic(m+n+2,0,n+m+1)); } return 0; }