有上下界的网络流2-有源汇带上下界网络流ZOJ3229
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有上下界的网络流2-有源汇带上下界网络流ZOJ3229相关的知识,希望对你有一定的参考价值。
ZOJ3229
题目大意:一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝可以和C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。
解题思路:
1.增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为正无穷的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。
2.然后从汇点sd到源点st连一条流量最大限制为正无穷的边,这样当前图就成了一个无源无汇的循环图。可以加两个超级源点和超级汇点ss和tt,像上一篇日志那样求无源汇带上下界的可行流。
3.如果求得有可行流,就把超级源点ss和超级汇点tt删掉,然后以st为源点、sd为汇点求当前图的最大流(因为上一步求得的只是可行流,里面还有很多边的流量未满)。当前求得的最大流即为解。如果为求得可行流则输出 -1 。
总结:对于有源汇带上下界的网络流求解,只需要从汇点连一条容量为正无穷的边到源点,构成一个无源汇的流量循环图,利用求无源汇带上下界网络流的方法求出一个可行流,再在求出的可行流上继续增广,最后求得的最大流即为有源汇带上下界的最大流。
贴个代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<vector> 6 #include<cstring> 7 #include<queue> 8 #define maxn 0x7fffffff 9 using namespace std; 10 struct edge{int to,cap,rev;}; 11 struct a_goddess{int m,low;}; 12 int st=1,sd=2000,ss=0,tt=2001; 13 vector <edge> E[2010]; 14 vector <a_goddess> day[400]; 15 int du[2010],ch[2010]; 16 int n,m; 17 void Add_Edge(int from,int to,int low,int up){ 18 edge t; 19 t.to=to,t.cap=up-low,t.rev=E[to].size(); 20 E[from].push_back(t); 21 t.to=from,t.cap=0,t.rev=E[from].size()-1; 22 E[to].push_back(t); 23 du[from]-=low,du[to]+=low; 24 } 25 void init(){ 26 memset(du,0,sizeof(du)); 27 int G,C,D,T,L,R; 28 for(int i=ss;i<=tt;i++) E[i].clear(); 29 for(int i=0;i<m;i++){ 30 scanf("%d",&G); 31 Add_Edge(500+i,sd,G,maxn); 32 } 33 for(int i=1;i<=n;i++){ 34 day[i].clear(); 35 scanf("%d%d",&C,&D); 36 Add_Edge(st,1+i,0,D); 37 for(int j=0;j<C;j++){ 38 scanf("%d%d%d",&T,&L,&R); 39 a_goddess t;t.m=T,t.low=L; 40 day[i].push_back(t); 41 Add_Edge(1+i,500+T,L,R); 42 } 43 } 44 } 45 bool bfs(int S,int T){ 46 memset(ch,-1,sizeof(ch)); 47 queue <int> que; 48 que.push(S);ch[S]=0; 49 while(que.size()){ 50 int t=que.front();que.pop(); 51 for(int i=0;i<E[t].size();i++){ 52 if(ch[E[t][i].to]==-1 && E[t][i].cap){ 53 ch[E[t][i].to]=ch[t]+1; 54 que.push(E[t][i].to); 55 } 56 } 57 } 58 return ch[T]>=0; 59 } 60 int dfs(int v,int T,int f){ 61 if(v==T) return f; 62 int r=0; 63 for(int i=0;i<E[v].size();i++){ 64 if (f==0) return r; 65 if(ch[E[v][i].to]==ch[v]+1){ 66 int u=dfs(E[v][i].to,T,min(f,E[v][i].cap)); 67 f-=u,r+=u; 68 E[v][i].cap-=u,E[E[v][i].to][E[v][i].rev].cap+=u; 69 } 70 } 71 return r; 72 } 73 int dinic(int S,int T){ 74 int f=0; 75 while(bfs(S,T)) f+=dfs(S,T,maxn); 76 return f; 77 } 78 bool check(){ 79 int f=0; 80 Add_Edge(sd,st,0,maxn); 81 for(int i=st;i<=sd;i++){ 82 if(du[i]>0) { 83 f+=du[i]; 84 Add_Edge(ss,i,0,du[i]); 85 } 86 if(du[i]<0) Add_Edge(i,tt,0,-du[i]); 87 } 88 return (dinic(ss,tt)==f); 89 } 90 int main(){ 91 while(scanf("%d%d",&n,&m)!=EOF){ 92 init(); 93 if(check()){ 94 for(int i=0;i<E[ss].size();i++){ 95 edge &t=E[ss][i]; 96 t.cap=0,E[t.to][t.rev].cap=0; 97 } 98 for(int i=0;i<E[tt].size();i++){ 99 edge &t=E[tt][i]; 100 t.cap=0,E[t.to][t.rev].cap=0; 101 } 102 printf("%d\n",dinic(st,sd)); 103 int ans[1100]; 104 for(int i=1;i<=n;i++){ 105 for(int j=0;j<E[1+i].size();j++){ 106 int to=E[i+1][j].to-500; 107 if(0<=to && to<m){ 108 ans[to]=E[E[1+i][j].to][E[1+i][j].rev].cap; 109 } 110 } 111 for(int j=0;j<day[i].size();j++) 112 printf("%d\n",ans[day[i][j].m]+day[i][j].low); 113 } 114 } 115 else{ 116 printf("%d\n",-1); 117 } 118 printf("\n"); 119 } 120 return 0; 121 }
ps:建图的时候用了一个小技巧,因为天数n<=365,女神数m<=1000,所以第i天用点 (1+i) 表示,第i个女神用点(500+i)表示。
以上是关于有上下界的网络流2-有源汇带上下界网络流ZOJ3229的主要内容,如果未能解决你的问题,请参考以下文章
ZOJ 3496 Assignment | 二分+有上下界网络流