从现在开始把每一个我觉得对我来说有新意的图论题目汇集下来
ps:今天发现了一个非常重要的常识我之前竟然一直没有注意到:对一个图第一遍跑最大流结果是最大流,如果还继续跑第二遍,结果会是0,很容易理解的地方,我竟然一直没有注意到。
由于目前基本只会网络流,就先来网络流的吧:
1.hdu2883
题意: 有一个烧烤机,每次最多能烤 m 块肉,现在有 n 个人来买烤肉,每个人到达时间为 si,离开时间为 ei,点的烤肉数量为 ci,点的烤肉所需烘烤时间为 di,
每个人要烤的肉可以分成若干份在同时烤,问是否存在一种方案可以满足所有顾客的需求。
分析: 将所有的到达时间和结束时间按升序排序,得到 x <= 2n-1 个时间区间。
建图:
s为源,t为汇,
每个顾客i作为一个结点并连边(s, i, ni*ti)
每个区间j作为一个结点并连边(j, t, (ej-sj)*M),其中sj, ej分别表示区间j的起始时间和终止时间
对任意顾客i和区间j,若 [sj, ej] 完全包含在 [si, ei] 之中,则连边(i, j, INF)
若最大流等于 ∑ni*ti 则是 Yes,否则是 No。
这题我是觉得不科学的,比如一组数据:
1 10
1 2 3 5
顾客要的东西要五分钟烤完,然后你特么的让人家等了2分钟就好了,你是怎么做到的呀O__O "…
当然,如果真的要较真的话这题我就不会做了(捂脸)
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 370000 8 #define MAXP 606 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 struct Cust 17 { 18 int start,last,stay; 19 }cust[MAXP]; 20 int head[MAXP]; 21 int time[MAXP*2]; 22 int stay[MAXP]; 23 int cur[MAXP]; 24 int pre[MAXP]; 25 int stack[MAXE]; 26 int used[MAXP]; 27 int ent; 28 int n,m,s,t; 29 int num; 30 void add(int start,int last,int f) 31 { 32 edge[ent].s=start; 33 edge[ent].t=last; 34 edge[ent].f=f; 35 edge[ent].next=head[start]; 36 head[start]=ent++; 37 edge[ent].s=last; 38 edge[ent].t=start; 39 edge[ent].f=0; 40 edge[ent].next=head[last]; 41 head[last]=ent++; 42 } 43 bool bfs(int S,int T) 44 { 45 memset(pre,-1,sizeof(pre)); 46 pre[S]=0; 47 queue<int>q; 48 q.push(S); 49 while(!q.empty()) 50 { 51 int temp=q.front(); 52 q.pop(); 53 for(int i=head[temp]; i!=-1; i=edge[i].next) 54 { 55 int temp2=edge[i].t; 56 if(pre[temp2]==-1&&edge[i].f) 57 { 58 pre[temp2]=pre[temp]+1; 59 q.push(temp2); 60 } 61 } 62 } 63 return pre[T]!=-1; 64 } 65 int dinic(int start,int last) 66 { 67 int flow=0,now; 68 while(bfs(start,last)) 69 { 70 int top=0; 71 memcpy(cur,head,sizeof(head)); 72 int u=start; 73 while(1) 74 { 75 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 76 { 77 int minn=INT_MAX; 78 for(int i=0; i<top; i++) 79 { 80 if(minn>edge[stack[i]].f) 81 { 82 minn=edge[stack[i]].f; 83 now=i; 84 } 85 } 86 for(int i=0;i<top;i++) 87 { 88 edge[stack[i]].f-=minn; 89 edge[stack[i]^1].f+=minn; 90 } 91 flow+=minn; 92 top=now; 93 u=edge[stack[top]].s; 94 } 95 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 96 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 97 break; 98 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 99 { 100 if(top==0)break; 101 pre[u]=-1; 102 u=edge[stack[--top]].s; 103 } 104 else//如果找到了继续运行 105 { 106 stack[top++]=cur[u]; 107 u=edge[cur[u]].t; 108 } 109 } 110 } 111 return flow; 112 } 113 int main() 114 { 115 while(~scanf("%d%d",&n,&m)) 116 { 117 int si,ni,ti; 118 int sum=0; 119 memset(head,-1,sizeof(head)); 120 ent=0; 121 s=0;t=3*n+1; 122 int tot=0; 123 for(int i=1;i<=n;i++) 124 { 125 scanf("%d%d%d%d",&cust[i].start,&ni,&cust[i].last,&ti); 126 cust[i].stay=ni*ti; 127 sum+=cust[i].stay; 128 time[tot++]=cust[i].start; 129 time[tot++]=cust[i].last; 130 } 131 sort(time,time+2*n); 132 for(int i=1;i<=n;i++) 133 add(s,i,cust[i].stay);//cout<<"road:"<<i<<" start:"<<cust[i].start<<" last:"<<cust[i].last<<endl;} 134 //cout<<"time[0]:"<<time[0]<<endl; 135 for(int i=1;i<2*n;i++) 136 { 137 //cout<<"time["<<i<<"]:"<<time[i]<<endl; 138 if(time[i]==time[i-1])continue; 139 for(int j=1;j<=n;j++) 140 { 141 if(cust[j].start<=time[i-1]&&cust[j].last>=time[i]) 142 add(j,n+i,m*(time[i]-time[i-1])); 143 } 144 add(n+i,t,m*(time[i]-time[i-1])); 145 } 146 //for(int i=0;i<ent;i++) 147 //if(edge[i].f)cout<<"s:"<<edge[i].s<<" t:"<<edge[i].t<<" f:"<<edge[i].f<<endl; 148 if(sum==dinic(s,t)) 149 printf("Yes\n"); 150 else printf("No\n"); 151 } 152 return 0; 153 }
2.poj3469
题意:一台双核电脑,给你多个任务,分别给出每个任务在第一个核和第二个核上运行的消耗。后面的m行输入是给出两个任务在两个不同核上运行需要付出的额外消耗。
建图:把每个任务作为节点,在超级源点与任务间的连一条边,其容量为给任务在核1上运行的消耗,在该任务节点与超级汇点之间连一条边,容量为该任务在核2上运行的消耗。
在任务之间连接无向边,容量为两个任务不在同一核上运行的额外消耗。
这题巧就巧在这句:在任务之间连接无向边,容量为两个任务不在同一核上运行的额外消耗。表示不是很理解,如果有大神在跪求带我装逼带我飞n(*≧▽≦*)n
代码:
1 #include<iostream> 2 #include<queue> 3 #include<cstring> 4 #include<cstdio> 5 #include<climits> 6 #define MAXE 1641000 7 #define MAXP 20010 8 #define Max(a,b) a>b?a:b 9 #define Min(a,b) a<b?a:b 10 using namespace std; 11 struct Edge 12 { 13 int s,t,f,next; 14 } edge[MAXE]; 15 int head[MAXP]; 16 int cur[MAXP]; 17 int pre[MAXP]; 18 int stack[MAXE]; 19 int used[MAXP]; 20 int ent; 21 int n,m,s,t; 22 int num; 23 void add(int start,int last,int f) 24 { 25 edge[ent].s=start; 26 edge[ent].t=last; 27 edge[ent].f=f; 28 edge[ent].next=head[start]; 29 head[start]=ent++; 30 edge[ent].s=last; 31 edge[ent].t=start; 32 edge[ent].f=0; 33 edge[ent].next=head[last]; 34 head[last]=ent++; 35 } 36 bool bfs(int S,int T) 37 { 38 memset(pre,-1,sizeof(pre)); 39 pre[S]=0; 40 queue<int>q; 41 q.push(S); 42 while(!q.empty()) 43 { 44 int temp=q.front(); 45 q.pop(); 46 for(int i=head[temp]; i!=-1; i=edge[i].next) 47 { 48 int temp2=edge[i].t; 49 if(pre[temp2]==-1&&edge[i].f) 50 { 51 pre[temp2]=pre[temp]+1; 52 q.push(temp2); 53 } 54 } 55 } 56 return pre[T]!=-1; 57 } 58 int dinic(int start,int last) 59 { 60 int flow=0,now; 61 while(bfs(start,last)) 62 { 63 int top=0; 64 memcpy(cur,head,sizeof(head)); 65 int u=start; 66 while(1) 67 { 68 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 69 { 70 int minn=INT_MAX; 71 for(int i=0; i<top; i++) 72 { 73 if(minn>edge[stack[i]].f) 74 { 75 minn=edge[stack[i]].f; 76 now=i; 77 } 78 } 79 for(int i=0;i<top;i++) 80 { 81 edge[stack[i]].f-=minn; 82 edge[stack[i]^1].f+=minn; 83 } 84 flow+=minn; 85 top=now; 86 u=edge[stack[top]].s; 87 } 88 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 89 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 90 break; 91 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 92 { 93 if(top==0)break; 94 pre[u]=-1; 95 u=edge[stack[--top]].s; 96 } 97 else//如果找到了继续运行 98 { 99 stack[top++]=cur[u]; 100 u=edge[cur[u]].t; 101 } 102 } 103 } 104 return flow; 105 } 106 int main() 107 { 108 while(~scanf("%d%d",&n,&m)) 109 { 110 memset(head,-1,sizeof(head)); 111 ent=0; 112 s=0;t=n+1; 113 int cost1,cost2; 114 for(int i=1;i<=n;i++) 115 { 116 scanf("%d%d",&cost1,&cost2); 117 add(s,i,cost1); 118 add(i,t,cost2); 119 } 120 int u,v,cost; 121 for(int i=1;i<=m;i++) 122 { 123 scanf("%d%d%d",&u,&v,&cost); 124 add(u,v,cost); 125 add(v,u,cost); 126 } 127 printf("%d\n",dinic(s,t)); 128 } 129 return 0; 130 }
3.hdu3061
中文题,思路就是最大权闭合图(妈蛋我竟然一直没看出来,没活路了,不管什么算法不刷题果然都特么不行呀/(ㄒoㄒ)/~~)
附带一点本人对最大权闭合图的理解:如果连边2->3流量为无穷,代表的不是2牵制3,而是3牵制2,即必须完成了3才能去完成2,而不是完成了2才能去完成3
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 250000 8 #define MAXP 500 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 struct Cust 17 { 18 int start,last,stay; 19 }cust[MAXP]; 20 int head[MAXP]; 21 int time[MAXP*2]; 22 int stay[MAXP]; 23 int cur[MAXP]; 24 int pre[MAXP]; 25 int stack[MAXE]; 26 int used[MAXP]; 27 int ent; 28 int n,m,s,t; 29 int num; 30 void add(int start,int last,int f) 31 { 32 edge[ent].s=start; 33 edge[ent].t=last; 34 edge[ent].f=f; 35 edge[ent].next=head[start]; 36 head[start]=ent++; 37 edge[ent].s=last; 38 edge[ent].t=start; 39 edge[ent].f=0; 40 edge[ent].next=head[last]; 41 head[last]=ent++; 42 } 43 bool bfs(int S,int T) 44 { 45 memset(pre,-1,sizeof(pre)); 46 pre[S]=0; 47 queue<int>q; 48 q.push(S); 49 while(!q.empty()) 50 { 51 int temp=q.front(); 52 q.pop(); 53 for(int i=head[temp]; i!=-1; i=edge[i].next) 54 { 55 int temp2=edge[i].t; 56 if(pre[temp2]==-1&&edge[i].f) 57 { 58 pre[temp2]=pre[temp]+1; 59 q.push(temp2); 60 } 61 } 62 } 63 return pre[T]!=-1; 64 } 65 int dinic(int start,int last) 66 { 67 int flow=0,now; 68 while(bfs(start,last)) 69 { 70 int top=0; 71 memcpy(cur,head,sizeof(head)); 72 int u=start; 73 while(1) 74 { 75 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 76 { 77 int minn=INT_MAX; 78 for(int i=0; i<top; i++) 79 { 80 if(minn>edge[stack[i]].f) 81 { 82 minn=edge[stack[i]].f; 83 now=i; 84 } 85 } 86 for(int i=0;i<top;i++) 87 { 88 edge[stack[i]].f-=minn; 89 edge[stack[i]^1].f+=minn; 90 } 91 flow+=minn; 92 top=now; 93 u=edge[stack[top]].s; 94 } 95 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 96 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 97 break; 98 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 99 { 100 if(top==0)break; 101 pre[u]=-1; 102 u=edge[stack[--top]].s; 103 } 104 else//如果找到了继续运行 105 { 106 stack[top++]=cur[u]; 107 u=edge[cur[u]].t; 108 } 109 } 110 } 111 return flow; 112 } 113 int main() 114 { 115 while(~scanf("%d%d",&n,&m)) 116 { 117 memset(head,-1,sizeof(head)); 118 ent=0;s=0;t=n+1; 119 int cost,sum=0; 120 for(int i=1;i<=n;i++) 121 { 122 scanf("%d",&cost); 123 if(cost>0) 124 { 125 add(s,i,cost); 126 sum+=cost; 127 } 128 else 129 add(i,t,-cost); 130 } 131 for(int i=1;i<=m;i++) 132 { 133 int u,v; 134 scanf("%d%d",&u,&v); 135 add(u,v,INT_MAX); 136 } 137 printf("%d\n",sum-dinic(s,t)); 138 } 139 return 0; 140 }
4.hdu1733&&poj3057
hdu1733题意:
有一个类似于迷宫搜索的图,‘.’代表的是无人的路,'X'代表有人的点,'#'代表此点不可通过,'@'代表门口。每个位置每一秒钟只能站一个人,每个位置到上下左右点的时间为1,问你所有人能不能出去,能出去输出所有人都出去的最小时间,否则输出-1.
建图:
1.拆点,对于第d天,每个点已被拆为d+1个点,下标代号成等差数列 公差n*m.
2.起始的时候,s向第0天的n*m中是人的点连条权值为1的边。
3.而后从小到大枚举每天,假设第t天,那么对于t-1天的点可以向四周扩展,或不动,向对应在第t天新加的点连条权为1的边,然后对第t天的n*m点,如果是出口则向汇点连条权值为1的边,然后从源点到汇点做网络流,如果ans等于人数,则break,return t。
4.对于没有解的情况,先dfs判断是否有解处理的。
ps:我提一个注意事项,虽然一般没傻逼到一定程度不会出现这种问题/(ㄒoㄒ)/~~,那就是:i,j对应的位置是(i-1)*m+j而不是(i-1)*n+j,因为这个我wa了20次,花了两天/(ㄒoㄒ)/~~
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 2000000 8 #define MAXP 200024 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 struct Point 17 { 18 int x,y; 19 }; 20 int head[MAXP]; 21 int cur[MAXP]; 22 int pre[MAXP]; 23 int stack[MAXE]; 24 int ent; 25 int n,m,s,t; 26 int num; 27 char map[18][18]; 28 bool can[18][18]; 29 bool visit[18][18]; 30 int dx[5]= {0,0,1,-1,0}; 31 int dy[5]= {1,-1,0,0,0}; 32 bool IN(int x,int y) 33 { 34 return x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]!='#'&&!visit[x][y]; 35 } 36 bool BFS(int x,int y) 37 { 38 memset(visit,false,sizeof(visit)); 39 Point temp,temp1; 40 temp.x=x; 41 temp.y=y; 42 queue<Point>q; 43 q.push(temp); 44 while(!q.empty()) 45 { 46 temp=q.front(); 47 q.pop(); 48 for(int i=0; i<4; i++) 49 { 50 temp1.x=temp.x+dx[i]; 51 temp1.y=temp.y+dy[i]; 52 if(can[temp1.x][temp1.y]||map[temp1.x][temp1.y]=='@') 53 { 54 can[x][y]=can[temp1.x][temp1.y]=true; 55 return true; 56 } 57 if(IN(temp1.x,temp1.y)) 58 { 59 q.push(temp1); 60 visit[temp1.x][temp.y]=true; 61 } 62 } 63 } 64 return false; 65 } 66 void add(int start,int last,int f) 67 { 68 edge[ent].s=start; 69 edge[ent].t=last; 70 edge[ent].f=f; 71 edge[ent].next=head[start]; 72 head[start]=ent++; 73 edge[ent].s=last; 74 edge[ent].t=start; 75 edge[ent].f=0; 76 edge[ent].next=head[last]; 77 head[last]=ent++; 78 } 79 bool bfs(int S,int T) 80 { 81 memset(pre,-1,sizeof(pre)); 82 pre[S]=0; 83 queue<int>q; 84 q.push(S); 85 while(!q.empty()) 86 { 87 int temp=q.front(); 88 q.pop(); 89 for(int i=head[temp]; i!=-1; i=edge[i].next) 90 { 91 int temp2=edge[i].t; 92 if(pre[temp2]==-1&&edge[i].f) 93 { 94 pre[temp2]=pre[temp]+1; 95 q.push(temp2); 96 } 97 } 98 } 99 return pre[T]!=-1; 100 } 101 int dinic(int start,int last) 102 { 103 int flow=0,now; 104 while(bfs(start,last)) 105 { 106 int top=0; 107 memcpy(cur,head,sizeof(head)); 108 int u=start; 109 while(1) 110 { 111 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 112 { 113 int minn=INT_MAX; 114 for(int i=0; i<top; i++) 115 { 116 if(minn>edge[stack[i]].f) 117 { 118 minn=edge[stack[i]].f; 119 now=i; 120 } 121 } 122 for(int i=0; i<top; i++) 123 { 124 edge[stack[i]].f-=minn; 125 edge[stack[i]^1].f+=minn; 126 } 127 flow+=minn; 128 top=now; 129 u=edge[stack[top]].s; 130 } 131 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 132 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 133 break; 134 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 135 { 136 if(top==0)break; 137 pre[u]=-1; 138 u=edge[stack[--top]].s; 139 } 140 else//如果找到了继续运行 141 { 142 stack[top++]=cur[u]; 143 u=edge[cur[u]].t; 144 } 145 } 146 } 147 return flow; 148 } 149 bool in(int x,int y) 150 { 151 return x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]!='#'; 152 } 153 int main() 154 { 155 while(~scanf("%d%d",&n,&m)) 156 { 157 num=0; 158 memset(can,false,sizeof(can)); 159 for(int i=1;i<=n;i++) 160 { 161 scanf("%s",map[i]+1); 162 } 163 bool ok=true; 164 for(int i=1; i<=n; i++) 165 { 166 for(int j=1; j<=m; j++) 167 { 168 if(map[i][j]=='X') 169 { 170 num++; 171 if(!BFS(i,j)) 172 { 173 ok=false; 174 } 175 } 176 } 177 } 178 if(num==0) 179 { 180 cout<<0<<endl; 181 continue; 182 } 183 if(!ok) 184 { 185 printf("-1\n"); 186 } 187 else 188 { 189 ent=0; 190 memset(head,-1,sizeof(head)); 191 s=0;t=200023; 192 for(int mid=1;; mid++) 193 { 194 int time=mid; 195 for(int i=1; i<=n; i++) 196 { 197 for(int j=1; j<=m; j++) 198 { 199 if(map[i][j]=='#')continue; 200 if(map[i][j]=='@') 201 { 202 add(time*2*n*m+(i-1)*m+j,t,1); 203 continue; 204 } 205 add((time-1)*2*m*n+(i-1)*m+j,(time*2-1)*n*m+(i-1)*m+j,1); 206 if(time==1&&map[i][j]=='X') 207 add(s,(i-1)*m+j,1); 208 for(int k=0; k<5; k++) 209 { 210 int x=i+dx[k]; 211 int y=j+dy[k]; 212 if(in(x,y)) 213 { 214 add((time*2-1)*n*m+(i-1)*m+j,time*2*n*m+(x-1)*m+y,1); 215 } 216 } 217 } 218 } 219 int temp=dinic(s,t); 220 if(temp==num) 221 { 222 cout<<mid<<endl; 223 break; 224 } 225 num-=temp; 226 } 227 } 228 } 229 return 0; 230 }
poj3057:
基本一样,两个不同:1.开始时每个空格位置都有一个人,2:后来除了门之外别的地方都可以有很多人,但是出去的话一次只能出去一个人。
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 1000000 8 #define MAXP 20000 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 struct Point 17 { 18 int x,y; 19 }; 20 int head[MAXP]; 21 int cur[MAXP]; 22 int pre[MAXP]; 23 int stack[MAXE]; 24 int ent; 25 int n,m,s,t; 26 int num; 27 char map[14][14]; 28 bool can[14][14]; 29 bool visit[14][14]; 30 int dx[5]= {0,0,1,-1,0}; 31 int dy[5]= {1,-1,0,0,0}; 32 bool IN(int x,int y) 33 { 34 return x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]!='X'&&!visit[x][y]; 35 } 36 bool BFS(int x,int y) 37 { 38 memset(visit,false,sizeof(visit)); 39 Point temp,temp1; 40 temp.x=x; 41 temp.y=y; 42 visit[x][y]=true; 43 queue<Point>q; 44 q.push(temp); 45 while(!q.empty()) 46 { 47 temp=q.front(); 48 q.pop(); 49 for(int i=0; i<4; i++) 50 { 51 temp1.x=temp.x+dx[i]; 52 temp1.y=temp.y+dy[i]; 53 if(can[temp1.x][temp1.y]||map[temp1.x][temp1.y]=='D') 54 { 55 can[x][y]=can[temp1.x][temp1.y]=true; 56 return true; 57 } 58 if(IN(temp1.x,temp1.y)) 59 { 60 q.push(temp1); 61 visit[temp1.x][temp.y]=true; 62 } 63 } 64 } 65 return false; 66 } 67 void add(int start,int last,int f) 68 { 69 edge[ent].s=start; 70 edge[ent].t=last; 71 edge[ent].f=f; 72 edge[ent].next=head[start]; 73 head[start]=ent++; 74 edge[ent].s=last; 75 edge[ent].t=start; 76 edge[ent].f=0; 77 edge[ent].next=head[last]; 78 head[last]=ent++; 79 } 80 bool bfs(int S,int T) 81 { 82 memset(pre,-1,sizeof(pre)); 83 pre[S]=0; 84 queue<int>q; 85 q.push(S); 86 while(!q.empty()) 87 { 88 int temp=q.front(); 89 q.pop(); 90 for(int i=head[temp]; i!=-1; i=edge[i].next) 91 { 92 int temp2=edge[i].t; 93 if(pre[temp2]==-1&&edge[i].f) 94 { 95 pre[temp2]=pre[temp]+1; 96 q.push(temp2); 97 } 98 } 99 } 100 return pre[T]!=-1; 101 } 102 int dinic(int start,int last) 103 { 104 int flow=0,now; 105 while(bfs(start,last)) 106 { 107 int top=0; 108 memcpy(cur,head,sizeof(head)); 109 int u=start; 110 while(1) 111 { 112 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 113 { 114 int minn=INT_MAX; 115 for(int i=0; i<top; i++) 116 { 117 if(minn>edge[stack[i]].f) 118 { 119 minn=edge[stack[i]].f; 120 now=i; 121 } 122 } 123 for(int i=0; i<top; i++) 124 { 125 edge[stack[i]].f-=minn; 126 edge[stack[i]^1].f+=minn; 127 } 128 flow+=minn; 129 top=now; 130 u=edge[stack[top]].s; 131 } 132 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 133 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 134 break; 135 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 136 { 137 if(top==0)break; 138 pre[u]=-1; 139 u=edge[stack[--top]].s; 140 } 141 else//如果找到了继续运行 142 { 143 stack[top++]=cur[u]; 144 u=edge[cur[u]].t; 145 } 146 } 147 } 148 return flow; 149 } 150 bool in(int x,int y) 151 { 152 return x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]!='X'; 153 } 154 int main() 155 { 156 int cas; 157 cin>>cas; 158 while(cas--) 159 { 160 scanf("%d%d",&n,&m); 161 num=0; 162 memset(can,false,sizeof(can)); 163 for(int i=1; i<=n; i++) 164 scanf("%s",map[i]+1); 165 bool ok=true; 166 for(int i=1; i<=n; i++) 167 { 168 for(int j=1; j<=m; j++) 169 { 170 if(map[i][j]=='.') 171 { 172 num++; 173 if(!BFS(i,j)) 174 { 175 ok=false; 176 } 177 } 178 /*if(map[i][j]=='D') 179 { 180 int oks=0; 181 for(int k=0;k<4;k++) 182 { 183 int x=i+dx[k]; 184 int y=j+dy[k]; 185 if(x<1||x>n||y<1||y>m)continue; 186 if(map[x][y]=='.') 187 oks++; 188 } 189 if(!oks) 190 map[i][j]='X'; 191 }*/ 192 } 193 } 194 if(num==0) 195 { 196 cout<<0<<endl; 197 continue; 198 } 199 if(!ok) 200 { 201 printf("impossible\n"); 202 } 203 else 204 { 205 memset(head,-1,sizeof(head)); 206 ent=0; 207 s=0; 208 t=19999; 209 for(int mid=1;; mid++) 210 { 211 //s=0; 212 //t=(mid+1)*n*m+1; 213 int time=mid; 214 for(int i=1; i<=n; i++) 215 { 216 for(int j=1; j<=m; j++) 217 { 218 if(map[i][j]=='X')continue; 219 if(time==1&&map[i][j]=='.') 220 add(s,(i-1)*m+j,1); 221 if(map[i][j]=='D') 222 { 223 add(time*n*m+(i-1)*m+j,t,1); 224 continue; 225 } 226 for(int k=0; k<5; k++) 227 { 228 int x=i+dx[k]; 229 int y=j+dy[k]; 230 if(in(x,y)) 231 { 232 add((time-1)*n*m+(i-1)*m+j,time*n*m+(x-1)*m+y,INT_MAX); 233 } 234 } 235 } 236 } 237 int temp=dinic(s,t); 238 if(temp==num) 239 { 240 cout<<mid<<endl; 241 break; 242 } 243 num-=temp; 244 } 245 } 246 } 247 return 0; 248 }
5.poj2396 有上下界有源汇的可行流
题意:
题目大意:有一个n*m的矩阵,每个位置(i,j)都有一个值,接下来输入n个数,每个数代表矩阵对应行的和,接下来输入m个数,每个数代表对应列的和。接下来有Q个操作,每个操作输入i j c val。
1、当c为'>': 表示第i行j列的数值要大于val。(实际下级要设为val+1)
2、当c为'<': 表示第i行j列的数值要小于val。(实际上界要设为val-1)
3、当c为'=': 表示第i行j列的数值要等于val。
让你求是否存在满足要求的矩阵,有则输出对应的矩阵,无则输出”impossible”。
注意:i=0||j=0表示那一行||那一列的每一个数都符合要求而不是所有数的和符合要求,妈的被坑了一天有木有,直接哭晕在厕所,真的抓狂了,英语差简直没活路呀
解题思路:
增设一源点s和一汇点d,源点s和每行的代表虚拟节点row[i]相连,权值为该行的权值总和,每列的代表虚拟节点和汇点d相连,权值为该列的权值总和,行连接列代表节点(i,j)即i行j列,它的容量上下界根据题目来确定。
连接一条附加边(d,s)容量为无穷,剩下的操作和无源汇有上下界的可行流一样。
注意:上下界不能出来一个就加一次这样会出错的,一定要把所有的操作全部搞定然后在确定最终的上下界,不然也是会wa的
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 5000000 8 #define MAXP 4400 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 int head[MAXP]; 17 int flow[MAXP]; 18 int degree[MAXP]; 19 int cur[MAXP]; 20 int pre[MAXP]; 21 int stack[MAXE]; 22 int maxn[MAXP]; 23 int minn[MAXP]; 24 int ent; 25 int n,m,s,t,supers,supert; 26 int num; 27 void add(int start,int last,int f) 28 { 29 edge[ent].s=start; 30 edge[ent].t=last; 31 edge[ent].f=f; 32 edge[ent].next=head[start]; 33 head[start]=ent++; 34 edge[ent].s=last; 35 edge[ent].t=start; 36 edge[ent].f=0; 37 edge[ent].next=head[last]; 38 head[last]=ent++; 39 } 40 bool bfs(int S,int T) 41 { 42 memset(pre,-1,sizeof(pre)); 43 pre[S]=0; 44 queue<int>q; 45 q.push(S); 46 while(!q.empty()) 47 { 48 int temp=q.front(); 49 q.pop(); 50 for(int i=head[temp]; i!=-1; i=edge[i].next) 51 { 52 int temp2=edge[i].t; 53 if(pre[temp2]==-1&&edge[i].f) 54 { 55 pre[temp2]=pre[temp]+1; 56 q.push(temp2); 57 } 58 } 59 } 60 return pre[T]!=-1; 61 } 62 int dinic(int start,int last) 63 { 64 int flow=0,now; 65 while(bfs(start,last)) 66 { 67 int top=0; 68 memcpy(cur,head,sizeof(head)); 69 int u=start; 70 while(1) 71 { 72 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 73 { 74 int minn=INT_MAX; 75 for(int i=0; i<top; i++) 76 { 77 if(minn>edge[stack[i]].f) 78 { 79 minn=edge[stack[i]].f; 80 now=i; 81 } 82 } 83 for(int i=0; i<top; i++) 84 { 85 edge[stack[i]].f-=minn; 86 edge[stack[i]^1].f+=minn; 87 } 88 flow+=minn; 89 top=now; 90 u=edge[stack[top]].s; 91 } 92 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 93 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 94 break; 95 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 96 { 97 if(top==0)break; 98 pre[u]=-1; 99 u=edge[stack[--top]].s; 100 } 101 else//如果找到了继续运行 102 { 103 stack[top++]=cur[u]; 104 u=edge[cur[u]].t; 105 } 106 } 107 } 108 return flow; 109 } 110 int main() 111 { 112 int cas; 113 cin>>cas; 114 while(cas--) 115 { 116 scanf("%d%d",&n,&m); 117 memset(head,-1,sizeof(head)); 118 memset(flow,0,sizeof(flow)); 119 memset(maxn,-1,sizeof(maxn)); 120 memset(minn,0,sizeof(minn)); 121 ent=0; 122 int ok=1; 123 s=0; 124 t=n+m+n*m+1; 125 supers=t+1; 126 supert=t+2; 127 int hang[200+1]; 128 int lie[200+1]; 129 int sumhang=0,sumlie=0; 130 memset(degree,0,sizeof(degree)); 131 int temp; 132 for(int i=1; i<=n; i++) 133 { 134 scanf("%d",&temp); 135 if(temp<0)ok=0; 136 hang[i]=temp; 137 sumhang+=temp; 138 degree[i]+=temp; 139 degree[s]-=temp; 140 add(s,i,0); 141 } 142 for(int i=n+1; i<=n+m; i++) 143 { 144 scanf("%d",&temp); 145 if(temp<0)ok=0; 146 lie[i-n]=temp; 147 sumlie+=temp; 148 degree[i]-=temp; 149 degree[t]+=temp; 150 add(i,t,0); 151 } 152 add(t,s,INT_MAX); 153 if(sumhang!=sumlie)ok=0; 154 scanf("%d",&num); 155 int u,v,sum; 156 char chara; 157 for(int i=1; i<=num; i++) 158 { 159 cin>>u>>v>>chara>>sum; 160 int s1=u,t1=u; 161 int s2=v,t2=v; 162 if(u==0)s1=1,t1=n; 163 if(v==0)s2=1,t2=m; 164 for(u=s1;u<=t1;u++) 165 for(v=s2;v<=t2;v++) 166 { 167 if(chara=='<') 168 { 169 if(sum<=0) 170 ok=0; 171 else 172 { 173 if(maxn[(u-1)*m+v]==-1) 174 maxn[(u-1)*m+v]=sum-1; 175 else maxn[(u-1)*m+v]=Min(maxn[(u-1)*m+v],sum-1); 176 } 177 } 178 else if(chara=='=') 179 { 180 if(sum<0)ok=0; 181 if(maxn[(u-1)*m+v]!=-1&&maxn[(u-1)*m+v]<sum)ok=0; 182 if(minn[(u-1)*m+v]>sum)ok=0; 183 maxn[(u-1)*m+v]=sum; 184 minn[(u-1)*m+v]=sum; 185 } 186 else if(chara=='>') 187 { 188 if(sum<0)continue; 189 if(sum>=hang[u]) 190 ok=0; 191 else 192 { 193 minn[(u-1)*m+v]=Max(minn[(u-1)*m+v],sum+1); 194 } 195 } 196 } 197 } 198 for(int i=1; i<=n; i++) 199 { 200 if(!ok)break; 201 for(int j=1; j<=m; j++) 202 { 203 if(maxn[(i-1)*m+j]==-1)maxn[(i-1)*m+j]=hang[i]; 204 if(maxn[(i-1)*m+j]<minn[(i-1)*m+j]) 205 { 206 ok=0; 207 break; 208 } 209 degree[i]-=minn[(i-1)*m+j]; 210 degree[j+n]+=minn[(i-1)*m+j]; 211 add(i,(i-1)*m+j+n+m,maxn[(i-1)*m+j]-minn[(i-1)*m+j]); 212 add((i-1)*m+j+n+m,j+n,maxn[(i-1)*m+j]-minn[(i-1)*m+j]); 213 flow[(i-1)*m+j]=minn[(i-1)*m+j]; 214 } 215 } 216 if(!ok) 217 { 218 printf("IMPOSSIBLE\n"); 219 if(cas)cout<<endl; 220 continue; 221 } 222 int all=0; 223 add(t,s,INT_MAX); 224 for(int i=s; i<=t; i++) 225 { 226 if(degree[i]<0) 227 add(i,supert,-degree[i]); 228 else if(degree[i]>0) 229 { 230 add(supers,i,degree[i]); 231 all+=degree[i]; 232 } 233 } 234 if(all==dinic(supers,supert)) 235 { 236 for(int j=1; j<=n; j++) 237 { 238 for(int i=head[j]; i!=-1; i=edge[i].next) 239 { 240 int aim=edge[i].t-n-m; 241 if(aim>0&&aim<=n*m) 242 { 243 flow[aim]+=edge[i^1].f; 244 } 245 } 246 } 247 for(int i=1; i<=n; i++) 248 { 249 for(int j=1; j<m; j++) 250 { 251 cout<<flow[(i-1)*m+j]<<" "; 252 } 253 cout<<flow[i*m]<<endl; 254 } 255 } 256 else cout<<"IMPOSSIBLE"<<endl; 257 if(cas)cout<<endl; 258 } 259 return 0; 260 }
hust1024 分层网络流(虽然这不是我做的第一道二分枚举可行流的题目,但是对于自己能30分钟之内独立的不看题解a掉他还是很激动的,当然必然用了最大流模板2333333333)
题意:
有n男n女,有m对男女喜欢对方,每个男的或者女的最多和k个他不喜欢的女的或男的跳舞,然后是输入m对喜欢对方的男女,然后叫你求出最多你给这么多人配几次对,每次配对都不能给一个人陪曾经给它配过的人。
思路:
网络流,关键在于建图。
把男性拆成两个点,分别放置在两个集合内,Xa和Xb,女性拆成两个点,分别放置在Ya和Yb内。Xa到Xb连接一条有向边,权值为k,Yb到Ya连接一条有向边,权值为k。当boy喜欢girl时,Xa和Ya之间连接一条对应的有向边权值为1,当boy不喜欢girl时,Xb和Yb连接一条对应的有向边,权值为1。
最后二分枚举可行解ans, 超级源点到Xa的每个点连接一条有向边,权值为ans,Ya内的每个点和超级汇点连接一条有向边,权值也为ans。最后流啊流,最大流满足max_flow==n*ans时,继续二分,直到找到最优解。(这应该会比较快,不过本人是从小到大枚举的)
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 500000 8 #define MAXP 450 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 int head[MAXP]; 17 int cur[MAXP]; 18 int pre[MAXP]; 19 int stack[MAXE]; 20 int ent; 21 int n,m,k,s,t; 22 int num; 23 int dx[4]={0,0,1,-1}; 24 int dy[4]={1,-1,0,0}; 25 bool in(int x,int y) 26 { 27 return x>=1&&x<=n&&y>=1&&y<=m; 28 } 29 void add(int start,int last,int f) 30 { 31 edge[ent].s=start; 32 edge[ent].t=last; 33 edge[ent].f=f; 34 edge[ent].next=head[start]; 35 head[start]=ent++; 36 edge[ent].s=last; 37 edge[ent].t=start; 38 edge[ent].f=0; 39 edge[ent].next=head[last]; 40 head[last]=ent++; 41 } 42 bool bfs(int S,int T) 43 { 44 memset(pre,-1,sizeof(pre)); 45 pre[S]=0; 46 queue<int>q; 47 q.push(S); 48 while(!q.empty()) 49 { 50 int temp=q.front(); 51 q.pop(); 52 for(int i=head[temp]; i!=-1; i=edge[i].next) 53 { 54 int temp2=edge[i].t; 55 if(pre[temp2]==-1&&edge[i].f) 56 { 57 pre[temp2]=pre[temp]+1; 58 q.push(temp2); 59 } 60 } 61 } 62 return pre[T]!=-1; 63 } 64 int dinic(int start,int last) 65 { 66 int flow=0,now; 67 while(bfs(start,last)) 68 { 69 int top=0; 70 memcpy(cur,head,sizeof(head)); 71 int u=start; 72 while(1) 73 { 74 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 75 { 76 int minn=INT_MAX; 77 for(int i=0; i<top; i++) 78 { 79 if(minn>edge[stack[i]].f) 80 { 81 minn=edge[stack[i]].f; 82 now=i; 83 } 84 } 85 for(int i=0; i<top; i++) 86 { 87 edge[stack[i]].f-=minn; 88 edge[stack[i]^1].f+=minn; 89 } 90 flow+=minn; 91 top=now; 92 u=edge[stack[top]].s; 93 } 94 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 95 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 96 break; 97 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 98 { 99 if(top==0)break; 100 pre[u]=-1; 101 u=edge[stack[--top]].s; 102 } 103 else//如果找到了继续运行 104 { 105 stack[top++]=cur[u]; 106 u=edge[cur[u]].t; 107 } 108 } 109 } 110 return flow; 111 } 112 int main() 113 { 114 int cas; 115 int option[101][101]; 116 scanf("%d",&cas); 117 while(cas--) 118 { 119 s=0;t=4*n+1; 120 memset(option,0,sizeof(option)); 121 scanf("%d%d%d",&n,&m,&k); 122 int u,v; 123 memset(head,-1,sizeof(head)); 124 ent=0; 125 for(int i=1;i<=m;i++) 126 { 127 scanf("%d%d",&u,&v); 128 option[u][v]=1; 129 add(u,v+n,1); 130 } 131 if(k!=0) 132 { 133 for(int i=1;i<=n;i++) 134 { 135 add(i,2*n+i,k); 136 add(3*n+i,n+i,k); 137 } 138 for(int i=1;i<=n;i++) 139 { 140 for(int j=1;j<=n;j++) 141 { 142 if(!option[i][j]) 143 { 144 add(2*n+i,3*n+j,1); 145 } 146 } 147 } 148 } 149 for(int i=1;i<=n+1;i++) 150 { 151 for(int j=1;j<=n;j++) 152 { 153 add(s,j,1); 154 add(j+n,t,1); 155 } 156 if(dinic(s,t)!=n) 157 { 158 printf("%d\n",i-1); 159 break; 160 } 161 } 162 } 163 return 0; 164 }
poj1815 相当水的一道最大流
题意加思路:
对于问至少删掉几个点使得S、T不联通,可以将每个点拆成i、i'两个点并连一条容量为1的i->i'的边,将其他关系依次补全后求最小割即可。
但是这个题目要求输出字典序最小的结果,那么就需要依次枚举每个点,如果删掉这个点之后最小割变小了,那么就说明这个点是最小割中的点,将其删除,否则就说名这个点不是最小割中的点,将其恢复。然后重复上面的操作就可以得到字典序最小的序列了。
不过这题为什么要拆点不容易想明白,我有点想法但又不知道怎么说,看来只好先记住了
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 1000000 8 #define MAXP 1024 9 #define Max(a,b) a>b?a:b 10 #define Min(a,b) a<b?a:b 11 using namespace std; 12 struct Edge 13 { 14 int s,t,f,next; 15 } edge[MAXE]; 16 struct Edges 17 { 18 int t,next; 19 } edges[MAXE]; 20 int head[MAXP]; 21 int isq[MAXP]; 22 int q[MAXP]; 23 int headedge[MAXP]; 24 int cur[MAXP]; 25 int pre[MAXP]; 26 int stack[MAXE]; 27 int ent,entedge; 28 int n,a,b,s,t; 29 void addedge(int start,int last) 30 { 31 edges[entedge].t=last; 32 edges[entedge].next=headedge[start]; 33 headedge[start]=entedge++; 34 edges[entedge].t=start; 35 edges[entedge].next=headedge[last]; 36 headedge[last]=entedge++; 37 } 38 void add(int start,int last,int f) 39 { 40 edge[ent].s=start; 41 edge[ent].t=last; 42 edge[ent].f=f; 43 edge[ent].next=head[start]; 44 head[start]=ent++; 45 edge[ent].s=last; 46 edge[ent].t=start; 47 edge[ent].f=0; 48 edge[ent].next=head[last]; 49 head[last]=ent++; 50 } 51 bool bfs(int S,int T) 52 { 53 memset(pre,-1,sizeof(pre)); 54 pre[S]=0; 55 queue<int>q; 56 q.push(S); 57 while(!q.empty()) 58 { 59 int temp=q.front(); 60 q.pop(); 61 for(int i=head[temp]; i!=-1; i=edge[i].next) 62 { 63 int temp2=edge[i].t; 64 if(pre[temp2]==-1&&edge[i].f) 65 { 66 pre[temp2]=pre[temp]+1; 67 q.push(temp2); 68 } 69 } 70 } 71 return pre[T]!=-1; 72 } 73 int dinic(int start,int last) 74 { 75 int flow=0,now; 76 while(bfs(start,last)) 77 { 78 int top=0; 79 memcpy(cur,head,sizeof(head)); 80 int u=start; 81 while(1) 82 { 83 if(u==last)//如果找到终点结束对中间路径进行处理并计算出该流 84 { 85 int minn=INT_MAX; 86 for(int i=0; i<top; i++) 87 { 88 if(minn>edge[stack[i]].f) 89 { 90 minn=edge[stack[i]].f; 91 now=i; 92 } 93 } 94 for(int i=0; i<top; i++) 95 { 96 edge[stack[i]].f-=minn; 97 edge[stack[i]^1].f+=minn; 98 } 99 flow+=minn; 100 top=now; 101 u=edge[stack[top]].s; 102 } 103 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) //找出从u点出发能到的边 104 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 105 break; 106 if(cur[u]==-1)//如果从该点未找到可行边,将该点标记并回溯 107 { 108 if(top==0)break; 109 pre[u]=-1; 110 u=edge[stack[--top]].s; 111 } 112 else//如果找到了继续运行 113 { 114 stack[top++]=cur[u]; 115 u=edge[cur[u]].t; 116 } 117 } 118 } 119 return flow; 120 } 121 int main() 122 { 123 while(~scanf("%d%d%d",&n,&a,&b)) 124 { 125 memset(head,-1,sizeof(head)); 126 ent=0; 127 entedge=0; 128 memset(headedge,-1,sizeof(headedge)); 129 memset(isq,0,sizeof(isq)); 130 s=0; 131 t=2*n+1; 132 add(s,a,INT_MAX); 133 add(b,t,INT_MAX); 134 add(a,a+n,INT_MAX); 135 int temp; 136 int ok=0; 137 for(int i=1; i<=n; i++) 138 { 139 if(i!=a&&i!=b) 140 add(i,i+n,1); 141 for(int j=1; j<=n; j++) 142 { 143 scanf("%d",&temp); 144 if(i==a&&j==b&&temp)ok=1; 145 if(temp&&i<j) 146 { 147 add(i+n,j,1); 148 add(j+n,i,1); 149 addedge(i,j); 150 } 151 } 152 } 153 temp=dinic(s,t); 154 if(ok)printf("NO ANSWER!"); 155 else 156 { 157 printf("%d\n",temp); 158 if(temp) 159 { 160 for(int i=1; i<=n; i++) 161 { 162 if(i==a||i==b)continue; 163 memset(head,-1,sizeof(head)); 164 ent=0; 165 add(s,a,INT_MAX); 166 add(b,t,INT_MAX); 167 add(a,a+n,INT_MAX); 168 for(int j=1; j<=n; j++) 169 { 170 if(j==i||isq[j])continue; 171 if(j!=a&&j!=b)add(j,j+n,1); 172 for(int k=headedge[j]; k!=-1; k=edges[k].next) 173 { 174 int temps=edges[k].t; 175 if(isq[temps]||temps==i)continue; 176 add(j+n,temps,1); 177 } 178 } 179 if(dinic(s,t)==temp-1) 180 { 181 isq[i]=1; 182 temp-=1; 183 } 184 if(temp==0)break; 185 } 186 int tot=0; 187 for(int i=1; i<=n; i++) 188 { 189 if(isq[i])q[tot++]=i; 190 } 191 for(int i=0; i<tot-1; i++) 192 printf("%d ",q[i]); 193 printf("%d\n",q[tot-1]); 194 } 195 } 196 } 197 return 0; 198 }
poj1422最小路径覆盖
题意:
一个镇里所有的路都是单向路且不会组成回路。
派一些伞兵去那个镇里,要到达所有的路口,有一些或者没有伞兵可以不去那些路口,只要其他人能完成这个任务。每个在一个路口着陆了的伞兵可以沿着街去到其他路口。我们的任务是求出去执行任务的伞兵最少可以是多少个。
思路:答案就是先拆点二分图,然后输出路口总数-最大流,原因应该就是每个匹配能减少一个需求。
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 920000 8 #define MAXP 100010 9 using namespace std; 10 struct Edge 11 { 12 int s,t,f,next; 13 }; 14 class Dinic 15 { 16 public: 17 Dinic(); 18 void insert(int st,int tt,int f); 19 bool bfs(); 20 void init(int st,int tt); 21 int dinic(); 22 void getCut();//获取割边S,先使用dinic获取最大流之后使用 23 void dfs(int start);//在getCut中用来求出S 24 private: 25 int s,t; 26 int ent; 27 int flow; 28 int *head; 29 int *cur; 30 int *pre; 31 int *stack; 32 Edge *edge; 33 int *getS; 34 }; 35 Dinic::Dinic() 36 { 37 head=new int[MAXP]; 38 cur=new int[MAXP]; 39 pre=new int[MAXP]; 40 stack=new int[MAXE]; 41 edge=new Edge[MAXE]; 42 getS=new int[MAXP]; 43 } 44 void Dinic::init(int st,int tt) 45 { 46 memset(head,-1,sizeof(int)*MAXP); 47 s=st; 48 t=tt; 49 ent=0; 50 flow=0; 51 } 52 void Dinic::insert(int st,int tt,int f) 53 { 54 edge[ent].s=st; 55 edge[ent].t=tt; 56 edge[ent].f=f; 57 edge[ent].next=head[st]; 58 head[st]=ent++; 59 edge[ent].s=tt; 60 edge[ent].t=st; 61 edge[ent].f=0; 62 edge[ent].next=head[tt]; 63 head[tt]=ent++; 64 } 65 bool Dinic::bfs() 66 { 67 memset(pre,-1,sizeof(int)*MAXP); 68 pre[s]=0; 69 queue<int>q; 70 q.push(s); 71 while(!q.empty()) 72 { 73 int temp1=q.front(); 74 q.pop(); 75 for(int i=head[temp1]; i!=-1; i=edge[i].next) 76 { 77 int temp2=edge[i].t; 78 if(pre[temp2]==-1&&edge[i].f) 79 { 80 pre[temp2]=pre[temp1]+1; 81 q.push(temp2); 82 } 83 } 84 } 85 return pre[t]!=-1; 86 } 87 int Dinic::dinic() 88 { 89 int now; 90 while(bfs()) 91 { 92 int top=0; 93 memcpy(cur,head,sizeof(int)*MAXP); 94 int u=s; 95 while(1) 96 { 97 if(u==t) 98 { 99 int minn=INT_MAX; 100 for(int i=0; i<top; i++) 101 { 102 if(minn>edge[stack[i]].f) 103 { 104 minn=edge[stack[i]].f; 105 now=i; 106 } 107 } 108 for(int i=0; i<top; i++) 109 { 110 edge[stack[i]].f-=minn; 111 edge[stack[i]^1].f+=minn; 112 } 113 flow+=minn; 114 top=now; 115 u=edge[stack[top]].s; 116 } 117 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) 118 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 119 break; 120 if(cur[u]==-1) 121 { 122 if(top==0)break; 123 pre[u]=-1; 124 u=edge[stack[--top]].s; 125 } 126 else 127 { 128 stack[top++]=cur[u]; 129 u=edge[cur[u]].t; 130 } 131 } 132 } 133 return flow; 134 } 135 void Dinic::getCut() 136 { 137 memset(getS,0,sizeof(int)*MAXP); 138 getS[s]=1; 139 dfs(s); 140 for(int i=0; i<MAXP; i++) 141 { 142 if(getS[i]) 143 cout<<i<<" "; 144 } 145 cout<<endl; 146 } 147 void Dinic::dfs(int start) 148 { 149 for(int i=head[start]; i!=-1; i=edge[i].next) 150 { 151 if(edge[i].f) 152 { 153 int temp=edge[i].t; 154 if(!getS[temp]) 155 { 156 getS[temp]; 157 dfs(temp); 158 } 159 } 160 } 161 } 162 int main() 163 { 164 int cas,n,m,s,t,f; 165 Dinic *temp=new Dinic(); 166 scanf("%d",&cas); 167 while(cas--) 168 { 169 scanf("%d%d",&n,&m); 170 temp->init(0,2*n+1); 171 for(int i=1; i<=n; i++) 172 { 173 temp->insert(0,i,1); 174 temp->insert(i+n,2*n+1,1); 175 } 176 for(int i=0; i<m; i++) 177 { 178 scanf("%d%d",&s,&t); 179 temp->insert(s,t+n,1); 180 } 181 cout<<(n-temp->dinic())<<endl; 182 } 183 delete temp; 184 return 0; 185 }
zoj3229
题意:
一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。
本来这题我是不想写博客的,毕竟这是我拿来学习的,然而因为一个坑,还是准备写一下,也许还能给别人一点提醒呢
这题的输出既不是输出屌丝每天给每个女神拍照的数量,也不是如果某天给某人拍照数量是0就不输出,而是只要题目中给出了就要输出,如果题目中没有给出就不输出。
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 900000 8 #define MAXP 2222 9 using namespace std; 10 int temps[400][1010]; 11 int id[400][1410]; 12 int flows[400][1500]; 13 struct Edge 14 { 15 int s,t,f,next; 16 }; 17 class Dinic 18 { 19 public: 20 Dinic(); 21 void init(int st,int tt);//初始化私有成员 22 void insert(int st,int tt,int f);//普通最大流插入边 23 void add_low_up(int st,int tt,int low,int up,int sign);//有上下界最大流插入边,其中sign是用来记录一些点对应的边 24 void deel();//有上下界最大流后期连超级源汇 25 bool bfs();//搜索增广路径 26 void dinic();//非递归求最大流 27 void reset(int st,int tt);//重置源汇 28 bool is_True();//判断是否存在满足上下界的最大流 29 void print_path(int a,int b,int c,int d);//视题目要求进行输出路径或者之类的处理 30 void getCut();//获取割边S,先使用dinic获取最大流之后使用 31 void dfs(int start);//在getCut中用来求出S 32 private: 33 int s,t; 34 int ent; 35 int flow; 36 int *head; 37 int *cur; 38 int *pre; 39 int *stack;//非递归dinic中模拟栈 40 Edge *edge; 41 int *getS;//纪录S; 42 int *degree;//有源汇有上下界最大流中使用,记录每个点的度数 43 int sum;//有源汇有上下界最大流中使用,纪录附加边满流时的流量 44 }; 45 Dinic::Dinic() 46 { 47 head=new int[MAXP]; 48 cur=new int[MAXP]; 49 pre=new int[MAXP]; 50 stack=new int[MAXE]; 51 edge=new Edge[MAXE]; 52 getS=new int[MAXP]; 53 degree=new int[MAXP]; 54 } 55 void Dinic::init(int st,int tt) 56 { 57 memset(head,-1,sizeof(int)*MAXP); 58 memset(degree,0,sizeof(int)*MAXP); 59 s=st; 60 t=tt; 61 ent=0; 62 flow=0; 63 sum=0; 64 } 65 void Dinic::insert(int st,int tt,int f) 66 { 67 edge[ent].s=st; 68 edge[ent].t=tt; 69 edge[ent].f=f; 70 edge[ent].next=head[st]; 71 head[st]=ent++; 72 edge[ent].s=tt; 73 edge[ent].t=st; 74 edge[ent].f=0; 75 edge[ent].next=head[tt]; 76 head[tt]=ent++; 77 } 78 void Dinic::add_low_up(int st,int tt,int low,int up,int sign) 79 { 80 degree[st]-=low,degree[tt]+=low; 81 insert(st,tt,up-low); 82 if(sign)id[st][tt]=ent-1; 83 } 84 void Dinic::deel() 85 { 86 for(int i=0; i<s; i++) 87 { 88 if(degree[i]<0) 89 insert(i,t,-degree[i]); 90 else sum+=degree[i],insert(s,i,degree[i]); 91 } 92 } 93 bool Dinic::bfs() 94 { 95 memset(pre,-1,sizeof(int)*MAXP); 96 pre[s]=0; 97 queue<int>q; 98 q.push(s); 99 while(!q.empty()) 100 { 101 int temp1=q.front(); 102 q.pop(); 103 for(int i=head[temp1]; i!=-1; i=edge[i].next) 104 { 105 int temp2=edge[i].t; 106 if(pre[temp2]==-1&&edge[i].f) 107 { 108 pre[temp2]=pre[temp1]+1; 109 q.push(temp2); 110 } 111 } 112 } 113 return pre[t]!=-1; 114 } 115 void Dinic::dinic() 116 { 117 int now; 118 while(bfs()) 119 { 120 int top=0; 121 memcpy(cur,head,sizeof(int)*MAXP); 122 int u=s; 123 while(1) 124 { 125 if(u==t) 126 { 127 int minn=INT_MAX; 128 for(int i=0; i<top; i++) 129 { 130 if(minn>edge[stack[i]].f) 131 { 132 minn=edge[stack[i]].f; 133 now=i; 134 } 135 } 136 for(int i=0; i<top; i++) 137 { 138 edge[stack[i]].f-=minn; 139 edge[stack[i]^1].f+=minn; 140 } 141 flow+=minn; 142 top=now; 143 u=edge[stack[top]].s; 144 } 145 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) 146 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 147 break; 148 if(cur[u]==-1) 149 { 150 if(top==0)break; 151 pre[u]=-1; 152 u=edge[stack[--top]].s; 153 } 154 else 155 { 156 stack[top++]=cur[u]; 157 u=edge[cur[u]].t; 158 } 159 } 160 } 161 } 162 void Dinic::reset(int st,int tt) 163 { 164 s=st,t=tt; 165 flow=0; 166 } 167 bool Dinic::is_True() 168 { 169 deel(); 170 dinic(); 171 if(sum==flow) 172 { 173 head[s]=-1,head[t]=-1; 174 reset(0,s-1); 175 dinic(); 176 return true; 177 } 178 else return false; 179 } 180 void Dinic::print_path(int a,int b,int c,int d) 181 { 182 if(is_True())//有满足上下界的最大流 183 { 184 printf("%d\n",flow); 185 memset(flows,0,sizeof(flows)); 186 /*for(int i=a;i<=b;i++) 187 for(int j=c;j<=d;j++) 188 if(id[i][j]) 189 printf("%d\n",temps[i][j-b]+edge[id[i][j]].f);*/ 190 for(int i=a;i<=b;i++) 191 { 192 for(int j=head[i];j!=-1;j=edge[j].next) 193 { 194 int temp=edge[j].t; 195 if(temp>=c&&temp<=d) 196 { 197 flows[i][temp]+=edge[j^1].f; 198 } 199 } 200 } 201 for(int i=a;i<=b;i++) 202 { 203 for(int j=c;j<=d;j++) 204 { 205 if(temps[i][j-b]!=-1) 206 printf("%d\n",flows[i][j]+temps[i][j-b]); 207 } 208 } 209 } 210 else//没有符合上下界的最大流 211 printf("-1\n"); 212 printf("\n"); 213 } 214 void Dinic::getCut() 215 { 216 memset(getS,0,sizeof(int)*MAXP); 217 getS[s]=1; 218 dfs(s); 219 for(int i=0; i<MAXP; i++) 220 { 221 if(getS[i]) 222 printf("%d ",i); 223 } 224 printf("\n"); 225 } 226 void Dinic::dfs(int start) 227 { 228 for(int i=head[start]; i!=-1; i=edge[i].next) 229 { 230 if(edge[i].f) 231 { 232 int temp=edge[i].t; 233 if(!getS[temp]) 234 { 235 getS[temp]; 236 dfs(temp); 237 } 238 } 239 } 240 } 241 int main() 242 { 243 int n,m,num,no,low,up,s,t; 244 Dinic *temp=new Dinic(); 245 while(~scanf("%d%d",&n,&m)) 246 { 247 memset(id,0,sizeof(id)); 248 memset(temps,-1,sizeof(temps)); 249 s=0,t=n+m+1; 250 temp->init(t+1,t+2); 251 for(int i=1; i<=m; i++) 252 { 253 scanf("%d",&num); 254 temp->add_low_up(n+i,t,num,INT_MAX,0); 255 } 256 for(int i=1; i<=n; i++) 257 { 258 scanf("%d%d",&num,&up); 259 temp->insert(s,i,up); 260 for(int j=1; j<=num; j++) 261 { 262 scanf("%d%d%d",&no,&low,&up); 263 temp->add_low_up(i,no+1+n,low,up,1); 264 temps[i][no+1]=low; 265 } 266 } 267 temp->insert(t,s,INT_MAX); 268 temp->print_path(1,n,n+1,n+m); 269 } 270 delete temp; 271 return 0; 272 }
poj3692
题意:已知班级有g个女孩和b个男孩,所有女生之间都相互认识,所有男生之间也相互认识,给出m对关系表示哪个女孩与哪个男孩认识。现在要选择一些学生来组成一个团,使得里面所有人都认识,求此团最大人数。
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<cstdio> 6 #include<climits> 7 #define MAXE 920000 8 #define MAXP 100010 9 using namespace std; 10 struct Edge 11 { 12 int s,t,f,next; 13 }; 14 class Dinic 15 { 16 public: 17 Dinic(); 18 void insert(int st,int tt,int f); 19 bool bfs(); 20 void init(int st,int tt); 21 int dinic(); 22 void getCut();//获取割边S,先使用dinic获取最大流之后使用 23 void dfs(int start);//在getCut中用来求出S 24 private: 25 int s,t; 26 int ent; 27 int flow; 28 int *head; 29 int *cur; 30 int *pre; 31 int *stack; 32 Edge *edge; 33 int *getS; 34 }; 35 Dinic::Dinic() 36 { 37 head=new int[MAXP]; 38 cur=new int[MAXP]; 39 pre=new int[MAXP]; 40 stack=new int[MAXE]; 41 edge=new Edge[MAXE]; 42 getS=new int[MAXP]; 43 } 44 void Dinic::init(int st,int tt) 45 { 46 memset(head,-1,sizeof(int)*MAXP); 47 s=st; 48 t=tt; 49 ent=0; 50 flow=0; 51 } 52 void Dinic::insert(int st,int tt,int f) 53 { 54 edge[ent].s=st; 55 edge[ent].t=tt; 56 edge[ent].f=f; 57 edge[ent].next=head[st]; 58 head[st]=ent++; 59 edge[ent].s=tt; 60 edge[ent].t=st; 61 edge[ent].f=0; 62 edge[ent].next=head[tt]; 63 head[tt]=ent++; 64 } 65 bool Dinic::bfs() 66 { 67 memset(pre,-1,sizeof(int)*MAXP); 68 pre[s]=0; 69 queue<int>q; 70 q.push(s); 71 while(!q.empty()) 72 { 73 int temp1=q.front(); 74 q.pop(); 75 for(int i=head[temp1]; i!=-1; i=edge[i].next) 76 { 77 int temp2=edge[i].t; 78 if(pre[temp2]==-1&&edge[i].f) 79 { 80 pre[temp2]=pre[temp1]+1; 81 q.push(temp2); 82 } 83 } 84 } 85 return pre[t]!=-1; 86 } 87 int Dinic::dinic() 88 { 89 flow=0; 90 int now; 91 while(bfs()) 92 { 93 int top=0; 94 memcpy(cur,head,sizeof(int)*MAXP); 95 int u=s; 96 while(1) 97 { 98 if(u==t) 99 { 100 int minn=INT_MAX; 101 for(int i=0; i<top; i++) 102 { 103 if(minn>edge[stack[i]].f) 104 { 105 minn=edge[stack[i]].f; 106 now=i; 107 } 108 } 109 for(int i=0; i<top; i++) 110 { 111 edge[stack[i]].f-=minn; 112 edge[stack[i]^1].f+=minn; 113 } 114 flow+=minn; 115 top=now; 116 u=edge[stack[top]].s; 117 } 118 for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next) 119 if(edge[i].f&&pre[edge[i].t]==pre[u]+1) 120 break; 121 if(cur[u]==-1) 122 { 123 if(top==0)break; 124 pre[u]=-1; 125 u=edge[stack[--top]].s; 126 } 127 else 128 { 129 stack[top++]=cur[u]; 130 u=edge[cur[u]].t; 131 } 132 } 133 } 134 return flow; 135 } 136 void Dinic::getCut() 137 { 138 memset(getS,0,sizeof(int)*MAXP); 139 getS[s]=1; 140 dfs(s); 141 for(int i=0;i<MAXP;i++) 142 { 143 if(getS[i]) 144 cout<<i<<" "; 145 } 146 cout<<endl; 147 } 148 void Dinic::dfs(int start) 149 { 150 for(int i=head[start];i!=-1;i=edge[i].next) 151 { 152 if(edge[i].f) 153 { 154 int temp=edge[i].t; 155 if(!getS[temp]) 156 { 157 getS[temp]; 158 dfs(temp); 159 } 160 } 161 } 162 } 163 int mp[210][210]; 164 int main() 165 { 166 int n,m,k,s,t; 167 int tot=0; 168 Dinic *temp = new Dinic(); 169 while(~scanf("%d%d%d",&n,&m,&k)&&(n||m||k)) 170 { 171 memset(mp,-1,sizeof(mp)); 172 for(int i=1;i<=k;i++) 173 { 174 scanf("%d%d",&s,&t); 175 mp[s][t]=0; 176 } 177 cout<<"Case "<<(++tot)<<": "; 178 temp->init(0,n+m+1); 179 for(int i=1;i<=n;i++) 180 { 181 temp->insert(0,i,1); 182 for(int j=1;j<=m;j++) 183 { 184 if(mp[i][j]) 185 temp->insert(i,j+n,1); 186 } 187 } 188 for(int i=1;i<=m;i++) 189 temp->insert(i+n,n+m+1,1); 190 printf("%d\n",n+m-temp->dinic()); 191 } 192 delete temp; 193 return 0; 194 }