2012多校联合赛第一场,第十题。
先不考虑可以修复的桥的性质, 则可以将模型简化为n个点的人通过有通过人数上限的有向边,到达一些有人数上限的特殊的边(隧道)。
可以建立最大流模型来求解, 增加一个源点S,和一个汇点T。 S向每个有人的点,连一条容量为人数的边, 图中普通的u->v的有向边,连一条u->v的流量为无穷的边, 桥的流量则为1。 对于隧道,每个隧道可以虚拟出一个点,如u->v的隧道,可以虚拟一个点x,连接u->x,x->v的流量无穷的边, 和x->T的流量为隧道人数上限的边, 求解最大流即可得到最大人数。
现在考虑桥的问题,题目中说明了桥最多只有12座,故可以2^12枚举修复哪些桥,不修复的桥没有花费,连接的边流量为1,要修复的桥则计算花费,边的流量为无穷,这样进行2^12次最大流就可以得到最优解。
看了题解后,建图,1Y。
2^12枚举,对桥建边很戏剧,用递归,很好玩。
发现我现在的编程状态很好,200行编下来,基本上想到什么就能编出来什么,网络流,递归,枚举。。以后只要相信自己就好。
相信自己的编码能力!!!
View Code
/* Problem : 4309 ( Seikimatsu Occult Tonneru ) Judge Status : Accepted RunId : 6291376 Language : G++ Author : 2010201211 Time : 1687MS Memory : 608K */ #include <iostream> #include <stdio.h> #include <string.h> using namespace std; #define E 30000 #define V 1102 #define inf 0xffff struct Edge{ int u,v,c,next; }edge[E]; int n,m,cnt; int cou; int dist[V]; int head[V]; int que[V]; int sta[V]; void init(){ cnt=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c){ edge[cnt].u=u;edge[cnt].v=v;edge[cnt].c=c; edge[cnt].next=head[u];head[u]=cnt++; edge[cnt].u=v;edge[cnt].v=u;edge[cnt].c=0; edge[cnt].next=head[v];head[v]=cnt++; } int dinic(int s,int t){ int ans=0; while(true){ int left,right,u,v; memset(dist,-1,sizeof(dist)); left=right=0; que[right++]=s; dist[s]=0; while(left<right){ u=que[left++]; for(int k=head[u];k!=-1;k=edge[k].next){ u=edge[k].u; v=edge[k].v; if(edge[k].c>0 && dist[v]==-1){ dist[v]=dist[u]+1; que[right++]=v; if(v==t){ left=right; break; } } } } if(dist[t]==-1) break; int top=0; int now=s; while(true){ if(now!=t){ int k; for(k=head[now];k!=-1;k=edge[k].next){ if(edge[k].c > 0 && dist[edge[k].u]+1==dist[edge[k].v]) break; } if(k!=-1){ sta[top++]=k; now=edge[k].v; } else{ if(top==0) break; dist[edge[sta[--top]].v]=-1; now=edge[sta[top]].u; } } else{ int flow=inf,ebreak; for(int i=0;i<top;i++){ if(flow>edge[sta[i]].c){ flow=edge[sta[i]].c; ebreak=i; } } ans+=flow; for(int i=0;i<top;i++){ edge[sta[i]].c-=flow; edge[sta[i]^1].c+=flow; } now = edge[sta[ebreak]].u; top = ebreak; } } } return ans; } int r,b,t; int p[V]; struct T{ int u,v,c; }road[E],bridge[E],tunnel[E]; bool fb[12]; struct R{ int num,cost; }res[5000]; void build(){ init(); for(int i=1;i<=n;i++){ addedge(0,i,p[i]); } for(int i=0;i<r;i++){ addedge(road[i].u,road[i].v,inf); } for(int i=0;i<t;i++){ addedge(tunnel[i].u,n+i+1,inf); addedge(n+i+1,tunnel[i].v,inf); addedge(n+i+1,n+t+1,tunnel[i].c); } } void dfs(int s){//2^12枚举,对桥建边。 if(s==b){ int sum=0; build(); for(int i=0;i<b;i++){ if(fb[i]==0){ addedge(bridge[i].u,bridge[i].v,1); sum+=0; } else{ addedge(bridge[i].u,bridge[i].v,inf); sum+=bridge[i].c; } } res[cou].num=dinic(0,n+t+1); res[cou++].cost=sum; return ; } fb[s]=0; dfs(s+1); fb[s]=1; dfs(s+1); } int main() { //freopen("in.txt","r",stdin); int a,bb,c,d; while(cin >> n >> m){ for(int i=1;i<=n;i++){ cin >> p[i]; } r=b=t=0; for(int i=1;i<=m;i++){ cin >> a >> bb >> c >> d; if(d<0){ tunnel[t].u=a; tunnel[t].v=bb; tunnel[t++].c=c; } if(d==0){ road[r].u=a; road[r++].v=bb; } if(d>0){ bridge[b].u=a; bridge[b].v=bb; bridge[b++].c=c; } } memset(fb,0,sizeof(fb)); for(int i=0;i<5000;i++){ res[i].cost=0; res[i].num =0; } cou=0; dfs(0); int minc=inf,maxc=-1; for(int i=0;i<cou;i++){ if(res[i].num>maxc){ maxc=res[i].num; } } for(int i=0;i<cou;i++){ if(res[i].num==maxc){ if(res[i].cost<minc){ minc=res[i].cost; } } } if(maxc==0) printf("Poor Heaven Empire\n"); else printf("%d %d\n",maxc,minc); } return 0; }