maxflow最大流/算法导论/挑战程序设计竞赛
流:
源点:s
汇点:t
每条边的容量:容量函数:c(u,v) (u,v)存在于边中
其图化模式为联通图,。
G的流是一个实值函数f: V*V->R且满足下列三个性质:
容量限制:对所有的 u,v ,f(u,v)<=c(u,v)
反对称性:对所有的 u,v f(u,v)=-f(v,u)
流守恒性:对所有的u属于V-{s,t}有1.所有流向v的流和为0,2.所以从v流出的流和为0。即流进和和流出的和都为0。
流的定义:|f|=从源点流向v的所有流之和。
主体:网络流。
让我们把运输流模拟成网络流吧。
超级源点和超级汇点。c为无穷
残量网络:cf(u,v)=c(u,v)-f(u,v) cf(v,u)=c(v,u)-f(v,u)
f(u,v)=-f(v,u) c(v,u)=0; cf(v,u)=f(u,v)
f为原网络中的流f'为残量网络中的流|f+f'|=|f|+|f'|
增广路径:cf(p)=min{cf(u,v):(u,v)在p上}
fp(u,v)=cf(p) (u,v)在p上/-cf(p) (v,u)在p上/0
|fp|=cf(p)>0如果能找到增广路径则继续找,而反向边的存在为了算法纠正自己的错误。
最大流最小割:一个网络的最大流等于其最小割。
所谓割(有向图)就是把网络划分为两个一个包含源点s和一个包含源点t的网络,然后求出所有割中的最小容量即最大流。
净流,和容量。
|f|=c(S,T)某个割即为最小割。
临接表建边的ek算法:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <string> 5 #include <algorithm> 6 #include <map> 7 #include <vector> 8 using namespace std; 9 const int N = 1100; 10 const int INF = 0x3f3f3f3f; 11 12 struct Node 13 { 14 int to;//终点 15 int cap; //容量 16 int rev; //反向边 17 }; 18 19 vector<Node> v[N]; 20 bool used[N]; 21 22 void add_Node(int from,int to,int cap) //重边情况不影响 23 { 24 v[from].push_back((Node){to,cap,v[to].size()}); 25 v[to].push_back((Node){from,0,v[from].size()-1}); 26 } 27 28 int dfs(int s,int t,int f) 29 { 30 if(s==t) 31 return f; 32 used[s]=true; 33 for(int i=0;i<v[s].size();i++) 34 { 35 Node &tmp = v[s][i]; //注意 36 if(used[tmp.to]==false && tmp.cap>0) 37 { 38 int d=dfs(tmp.to,t,min(f,tmp.cap)); 39 if(d>0) 40 { 41 tmp.cap-=d; 42 v[tmp.to][tmp.rev].cap+=d; 43 return d; 44 } 45 } 46 } 47 return 0; 48 } 49 50 int max_flow(int s,int t) 51 { 52 int flow=0; 53 for(;;){ 54 memset(used,false,sizeof(used)); 55 int f=dfs(s,t,INF); 56 if(f==0) 57 return flow; 58 flow+=f; 59 } 60 } 61 int main() 62 { 63 int n,m; 64 while(~scanf("%d%d",&n,&m)) 65 { 66 memset(v,0,sizeof(v)); 67 for(int i=0;i<n;i++) 68 { 69 int x,y,z; 70 scanf("%d%d%d",&x,&y,&z); 71 add_Node(x,y,z); 72 } 73 printf("%d\n",max_flow(1,m)); 74 } 75 }
临接矩阵建边的ek算法实现:
1 #include <cstdio> 2 #include <vector> 3 #include <iostream> 4 #include <queue> 5 #include <cstring> 6 7 using namespace std; 8 9 #define maxn 105 10 #define inf 0x3f3f3f3f 11 12 int map[maxn][maxn]; 13 int flow[maxn][maxn]; 14 int a[maxn],p[maxn]; 15 int n; 16 17 18 int Ford_fullkerson(int s,int t) 19 { 20 int f=0,u,v; 21 queue<int> qq; 22 memset(flow,0,sizeof(flow)); 23 while(1) 24 { 25 memset(a,0,sizeof(a)); 26 a[s]=inf; 27 qq.push(s); 28 while(!qq.empty()) 29 { 30 u=qq.front();qq.pop(); 31 for(v=1;v<=n;v++) 32 { 33 if(!a[v] && map[u][v]>flow[u][v]) 34 { 35 p[v]=u;qq.push(v); 36 a[v]=a[u]<map[u][v]-flow[u][v]?a[u]:(map[u][v]-flow[u][v]); 37 } 38 } 39 } 40 if(a[t]==0) 41 return f; 42 for(int i=t;i!=s;i=p[i]) 43 { 44 flow[i][p[i]]-=a[t]; 45 flow[p[i]][i]+=a[t]; 46 } 47 f+=a[t]; 48 } 49 } 50 51 int main() 52 { 53 int s,t,c; 54 int cas=1; 55 while(scanf("%d",&n),n) 56 { 57 int x,y,z; 58 memset(map,0,sizeof(map)); 59 scanf("%d%d%d",&s,&t,&c); 60 for(int i=0;i<c;i++) 61 { 62 scanf("%d%d%d",&x,&y,&z); 63 map[x][y]+=z; 64 map[y][x]+=z; 65 } 66 printf("Network %d\nThe bandwidth is %d.\n\n",cas++,Ford_fullkerson(s,t)); 67 } 68 return 0; 69 }
Dinic算法实现O(|E|*|V*V|)
1 #include <cstdio> 2 #include <vector> 3 #include <iostream> 4 #include <queue> 5 #include <cstring> 6 #include <algorithm> 7 using namespace std; 8 const int N = 300; 9 const int MAX = 0x3f3f3f3f; 10 #define Min(a,b) a>b?b:a 11 int dis[N]; 12 int map[N][N]; 13 int m,n; 14 int BFS() //构建层次网络 15 { 16 memset(dis,-1,sizeof(dis)); 17 dis[1]=0; 18 queue<int> v; 19 v.push(1); 20 while(!v.empty()) 21 { 22 int x=v.front();v.pop(); 23 for(int i=1;i<=n;i++) 24 { 25 if(dis[i]<0 && map[x][i]>0) 26 { 27 dis[i]=dis[x]+1; 28 v.push(i); 29 } 30 } 31 } 32 if(dis[n]>0) 33 return 1; 34 else 35 return 0; 36 } 37 int DFS(int x,int low)//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量 38 { 39 int i,a=0; 40 if (x==n) 41 return low;//是汇点 42 for(i=1;i<=n;i++) 43 if (map[x][i] >0 //联通 44 && dis[i]==dis[x]+1 //是分层图的下一层 45 &&(a=DFS(i,min(low,map[x][i]))))//能到汇点(a <> 0) 46 { 47 map[x][i] -= a; 48 map[i][x] += a; 49 return a; 50 } 51 return 0; 52 53 } 54 55 int main() 56 { 57 while(~scanf("%d%d",&m,&n)) 58 { 59 memset(map,0,sizeof(map)); 60 for(int i=0;i<m;i++){ 61 int x,y,z; 62 scanf("%d%d%d",&x,&y,&z); 63 map[x][y]+=z; 64 } 65 int ans=0,tmp; 66 while(BFS()) 67 { 68 while(tmp=DFS(1,0x3f3f3f3f)) 69 ans+=tmp; 70 } 71 printf("%d\n",ans); 72 } 73 return 0; 74 }
uva,820
因为点100个,可能存在多条边,且是无向图。那么O(|E|*|E|*|V|)的复杂度足够了。可以用dinic算法实现,更快。
而且这道题目不能用临接表来做,(因为后向边的编号不知道怎么处理)更正可以处理= =。
因为这里有重边= =不重边情况下不影响,因为编号不同。
因该是因为对于临接表来说每一条边都要建而不是想临接矩阵一样相加就可以了,而且stl储存等等也要花费时间,这里每两个点之间多一条边就是多一对边,至少:200*200*100超时了。
1 #include <cstdio> 2 #include <vector> 3 #include <iostream> 4 #include <queue> 5 #include <cstring> 6 7 using namespace std; 8 9 #define maxn 105 10 #define inf 0x3f3f3f3f 11 12 int map[maxn][maxn]; 13 int flow[maxn][maxn]; 14 int a[maxn],p[maxn]; 15 int n; 16 17 18 int Ford_fullkerson(int s,int t) 19 { 20 int f=0,u,v; 21 queue<int> qq; 22 memset(flow,0,sizeof(flow)); 23 while(1) 24 { 25 memset(a,0,sizeof(a)); 26 a[s]=inf; 27 qq.push(s); 28 while(!qq.empty()) 29 { 30 u=qq.front();qq.pop(); 31 for(v=1;v<=n;v++) 32 { 33 if(!a[v] && map[u][v]>flow[u][v]) 34 { 35 p[v]=u;qq.push(v); 36 a[v]=a[u]<map[u][v]-flow[u][v]?a[u]:(map[u][v]-flow[u][v]); 37 } 38 } 39 } 40 if(a[t]==0) 41 return f; 42 for(int i=t;i!=s;i=p[i]) 43 { 44 flow[i][p[i]]-=a[t]; 45 flow[p[i]][i]+=a[t]; 46 } 47 f+=a[t]; 48 } 49 } 50 51 int main() 52 { 53 int s,t,c; 54 int cas=1; 55 while(scanf("%d",&n),n) 56 { 57 int x,y,z; 58 memset(map,0,sizeof(map)); 59 scanf("%d%d%d",&s,&t,&c); 60 for(int i=0;i<c;i++) 61 { 62 scanf("%d%d%d",&x,&y,&z); 63 map[x][y]+=z; 64 map[y][x]+=z; 65 } 66 printf("Network %d\nThe bandwidth is %d.\n\n",cas++,Ford_fullkerson(s,t)); 67 } 68 return 0; 69 }
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 #define maxn 205 9 #define inf 0x3f3f3f3f 10 11 struct edge 12 { 13 int to,cap,rev; 14 }; 15 16 vector<edge> G[maxn]; 17 bool used[maxn]; 18 int n; 19 20 void add_edge(int from,int to,int cap) 21 { 22 G[from].push_back((edge){to,cap,(int)G[to].size()}); 23 G[to].push_back((edge){from,0,(int)G[from].size()-1}); 24 } 25 26 27 int dfs(int s,int t,int f) 28 { 29 if(s==t) 30 return f; 31 used[s]=true; 32 for(int i=0;i<G[s].size();i++) 33 { 34 edge &e=G[s][i]; 35 if(!used[e.to] && e.cap>0) 36 { 37 int d=dfs(e.to,t,min(f,e.cap)); 38 if(d>0) 39 { 40 e.cap-=d; 41 G[e.to][e.rev].cap+=d; 42 return d; 43 } 44 } 45 } 46 return 0; 47 } 48 49 int max_flow(int s,int t) 50 { 51 int flow=0; 52 for(;;) 53 { 54 memset(used,false,sizeof(used)); 55 int f=dfs(s,t,inf); 56 if(f==0) 57 return flow; 58 flow+=f; 59 } 60 } 61 62 int main() 63 { 64 int s,t,c; 65 int cas=1; 66 while(scanf("%d",&n),n) 67 { 68 int x,y,z; 69 scanf("%d%d%d",&s,&t,&c); 70 for(int i=0;i<c;i++) 71 { 72 scanf("%d%d%d",&x,&y,&z); 73 add_edge(x,y,z); 74 add_edge(y,x,z); 75 } 76 printf("Network %d\nThe bandwidth is %d.\n\n",cas++,max_flow(s,t)); 77 } 78 return 0; 79 }
uva,753
插头和插线板,还有转接器。
以0为源点,然后向所有插线板连一条容量为1的边,最后一个点为汇点,所有电器向汇点连一条容量为1的边。
转换器的插线板和插头之间连一条容量无限的边,因为数量无限。
做简化处理:因为插线板连向电器再连向汇点和插线板直接连向汇点一样,所以中间可以省略。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 7 using namespace std; 8 #define maxn 1005 9 #define inf 0x3f3f3f3f 10 11 int n,m,k,tmp; 12 int g[maxn][maxn]; 13 int f[maxn][maxn]; 14 int t[maxn],rec[maxn]; 15 char name[maxn][maxn]; 16 17 18 int find(char *ch) 19 { 20 for(int i=0;i<tmp;i++) 21 { 22 if(strcmp(name[i],ch)==0) 23 return i; 24 } 25 strcpy(name[tmp],ch); 26 return tmp++; 27 } 28 29 void init() 30 { 31 tmp=1; 32 memset(g,0,sizeof(g)); 33 memset(f,0,sizeof(f)); 34 memset(name,0,sizeof(name)); 35 36 37 char ch[maxn],na[maxn]; 38 scanf("%d",&n); 39 for(int i=0;i<n;i++) 40 { 41 scanf("%s",ch); 42 int p=find(ch); 43 g[0][p]++; 44 } 45 46 scanf("%d",&m); 47 for(int i=0;i<m;i++) 48 { 49 scanf("%s%s",na,ch); 50 int p=find(ch); 51 rec[i]=p; 52 } 53 54 scanf("%d",&k); 55 for(int i=0;i<k;i++) 56 { 57 scanf("%s%s",ch,na); 58 int p=find(ch),q=find(na); 59 g[q][p]=inf; 60 } 61 } 62 63 int solve() 64 { 65 for(int i=0;i<m;i++) 66 g[rec[i]][tmp]++; 67 int f1=0,u,v; 68 queue<int> qq; 69 70 while(1) 71 { 72 memset(t,0,sizeof(t)); 73 memset(rec,0,sizeof(rec)); 74 t[0]=inf; 75 qq.push(0); 76 while(!qq.empty()) 77 { 78 u=qq.front();qq.pop(); 79 for(v=1;v<=tmp;v++) 80 { 81 if(!t[v] && g[u][v]>f[u][v]) 82 { 83 rec[v]=u;qq.push(v); 84 t[v]=min(t[u],g[u][v]-f[u][v]); 85 } 86 } 87 } 88 if(t[tmp]==0) 89 return f1; 90 for(int i=tmp;i;i=rec[i]) 91 { 92 f[i][rec[i]]-=t[tmp]; 93 f[rec[i]][i]+=t[tmp]; 94 } 95 f1+=t[tmp]; 96 } 97 } 98 99 int main() 100 { 101 int cas; 102 scanf("%d",&cas); 103 while(cas--) 104 { 105 init(); 106 printf("%d\n",m-solve()); 107 if(cas) printf("\n"); 108 } 109 return 0; 110 }
看来我没有说错。就是这个样子。😄
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 7 using namespace std; 8 #define maxn 1005 9 #define inf 0x3f3f3f3f 10 11 int n,m,k,tmp; 12 int g[maxn][maxn]; 13 int f[maxn][maxn]; 14 int t[maxn],rec[maxn]; 15 char name[maxn][maxn]; 16 17 18 int find(char *ch) 19 { 20 for(int i=0;i<tmp;i++) 21 { 22 if(strcmp(name[i],ch)==0) 23 return i; 24 } 25 strcpy(name[tmp],ch); 26 return tmp++; 27 } 28 29 void init() 30 { 31 tmp=1; 32 memset(g,0,sizeof(g)); 33 memset(f,0,sizeof(f)); 34 memset(name,0,sizeof(name)); 35 36 37 char ch[maxn],na[maxn]; 38 scanf("%d",&n); 39 for(int i=0;i<n;i++) 40 { 41 scanf("%s",ch); 42 int p=find(ch); 43 g[0][p]++; 44 } 45 46 scanf("%d",&m); 47 for(int i=0;i<m;i++) 48 { 49 scanf("%s%s",na,ch); 50 int p=find(ch),q=find(na); 51 g[p][q]++; 52 rec[i]=q; 53 } 54 55 scanf("%d",&k); 56 for(int i=0;i<k;i++) 57 { 58 scanf("%s%s",ch,na); 59 int p=find(ch),q=find(na); 60 g[q][p]=inf; 61 } 62 } 63 64 int solve() 65 { 66 for(int i=0;i<m;i++) 67 g[rec[i]][tmp]++; 68 int f1=0,u,v; 69 queue<int> qq; 70 71 while(1) 72 { 73 memset(t,0,sizeof(t)); 74 memset(rec,0,sizeof(rec)); 75 t[0]=inf; 76 qq.push(0); 77 while(!qq.empty()) 78 { 79 u=qq.front();qq.pop(); 80 for(v=1;v<=tmp;v++) 81 { 82 if(!t[v] && g[u][v]>f[u][v]) 83 { 84 rec[v]=u;qq.push(v); 85 t[v]=min(t[u],g[u][v]-f[u][v]); 86 } 87 } 88 } 89 if(t[tmp]==0) 90 return f1; 91 for(int i=tmp;i;i=rec[i]) 92 { 93 f[i][rec[i]]-=t[tmp]; 94 f[rec[i]][i]+=t[tmp]; 95 } 96 f1+=t[tmp]; 97 } 98 } 99 100 int main() 101 { 102 int cas; 103 scanf("%d",&cas); 104 while(cas--) 105 { 106 init(); 107 printf("%d\n",m-solve()); 108 if(cas) printf("\n"); 109 } 110 return 0; 111 }
临接表的好处在于e为正向边,e^1为它的反向边。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <queue> 5 #include <cstring> 6 7 using namespace std; 8 9 #define maxn 550 10 #define maxm 50010 11 #define inf 0x3f3f3f3f 12 13 int n,m,k,s,t,cnt,edgenum; 14 int first[maxn]; 15 16 struct edge 17 { 18 int u,v,f,cap,next; 19 }e[maxm]; 20 21 struct divice 22 { 23 char s1[30],s2[maxn]; 24 }d[maxn]; 25 26 struct adapter 27 { 28 char s1[30],s2[30]; 29 }c[maxn]; 30 31 struct receptacle 32 { 33 char s[30]; 34 }r[maxn]; 35 36 37 void add_edge(int u,int v,int cap) 38 { 39 int t; 40 e[edgenum].u=u,e[edgenum].v=v; 41 e[edgenum].f=0,e[edgenum].cap=cap; 42 e[edgenum].next=first[u]; 43 first[u]=edgenum++; 44 45 t=u;u=v;v=t; 46 47 e[edgenum].u=u,e[edgenum].v=v; 48 e[edgenum].f=0,e[edgenum].cap=0; 49 e[edgenum].next=first[u]; 50 first[u]=edgenum++; 51 } 52 53 54 void input() 55 { 56 scanf("%d",&n); 57 for(int i=1;i<=n;i++) 58 scanf("%s",r[i].s); 59 scanf("%d",&m); 60 for(int i=1;i<=m;i++) 61 scanf("%s%s",d[i].s1,d[i].s2); 62 63 scanf("%d",&k); 64 for(int i=1;i<=k;i++) 65 scanf("%s%s",c[i].s1,c[i].s2); 66 67 cnt=n+k+m; 68 } 69 70 void init() 71 { 72 memset(first,-1,sizeof(first)); 73 74 s=0,t=cnt+1; 75 76 for(int i=1;i<=m;i++) 77 { 78 add_edge(s,i,1); 79 80 for(int j=1;j<=k;j++) 81 if(!strcmp(d[i].s2,c[j].s1)) 82 add_edge(i,m+j,inf); 83 84 for(int j=1;j<=n;j++) 85 if(!strcmp(d[i].s2,r[j].s)) 86 add_edge(i,j+m+k,1); 87 } 88 89 for(int i=1;i<=k;i++) 90 { 91 for(int j=1;j<=k;j++) 92 { 93 if(i!=j && !strcmp(c[i].s2,c[j].s1)) 94 add_edge(i+m,j+m,inf); 95 } 96 97 for(int j=1;j<=n;j++) 98 { 99 if(!strcmp(c[i].s2,r[j].s)) 100 add_edge(i+m,j+m+k,inf); 101 } 102 } 103 104 for(int i=1;i<=n;i++) 105 add_edge(i+m+k,t,1); 106 } 107 108 void ek() 109 { 110 queue<int> q; 111 int f1=0; 112 int a[maxn],path[maxn]; 113 114 while(1) 115 { 116 memset(a,0,sizeof(a)); 117 memset(path,-1,sizeof(path)); 118 a[s]=inf;q.push(s); 119 while(!q.empty()) 120 { 121 int u=q.front();q.pop(); 122 for(int i=first[u];i!=-1;i=e[i].next)//临接表所包含元素 123 { 124 int v=e[i].v; 125 if(!a[v] && e[i].cap>e[i].f) 126 { 127 a[v]=min(a[u],e[i].cap-e[i].f); 128 path[v]=i; 129 q.push(v); 130 } 131 } 132 } 133 134 if(!a[t]) 135 break; 136 137 for(int i=path[t];i!=-1;i=path[e[i].u])//增广 138 { 139 e[i].f+=a[t]; 140 e[i^1].f-=a[t]; 141 } 142 f1+=a[t]; 143 } 144 printf("%d\n",m-f1); 145 } 146 147 int main() 148 { 149 int cas; 150 scanf("%d",&cas); 151 while(cas--) 152 { 153 input(); 154 init(); 155 ek(); 156 if(cas) printf("\n"); 157 } 158 return 0; 159 }
uva,563
因为一个交叉路口只能被经过一次,所以拆点然后跑一遍ek。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <vector> 5 using namespace std; 6 7 #define INF 9999999 8 #define MAX 2*52*52+10 9 vector<int> edge[MAX]; 10 int cap[MAX][MAX], flow[MAX][MAX]; 11 int bottleneck[MAX], pre[MAX]; 12 13 void BuildGraph(int S, int T, int H, int W); 14 int MaxFlow(int S, int T, int H, int W); 15 16 int main() 17 { 18 int Case, H, W, B, x, y; 19 scanf("%d", &Case); 20 while (Case--) { 21 scanf("%d %d %d", &H, &W, &B); 22 23 int S = 0, 24 T = 1; 25 26 for (int i = 0; i <= 2*(H+1)*W; ++i) edge[i].clear(); 27 memset(cap, 0, sizeof(cap)); 28 memset(flow, 0, sizeof(flow)); 29 30 BuildGraph(S, T, H, W); 31 32 for (int i = 0; i < B; ++i) { 33 scanf("%d %d", &x, &y); 34 cap[S][(x*H+y)*2] = 1; 35 edge[S].push_back((x*H+y)*2); 36 } 37 38 if (MaxFlow(S, T, H, W) == B) puts("possible"); 39 else puts("not possible"); 40 } 41 } 42 void BuildGraph(int S, int T, int H, int W) 43 { 44 for (int i = 1; i <= H; ++i) { 45 for (int j = 1; j <= W; ++j) { 46 cap[(i*H+j)*2][(i*H+j)*2+1] = 1; 47 edge[(i*H+j)*2].push_back((i*H+j)*2+1); 48 49 cap[(i*H+j)*2+1][((i-1)*H+j)*2] = 1; 50 edge[(i*H+j)*2+1].push_back(((i-1)*H+j)*2); 51 52 cap[(i*H+j)*2+1][((i+1)*H+j)*2] = 1; 53 edge[(i*H+j)*2+1].push_back(((i+1)*H+j)*2); 54 55 cap[(i*H+j)*2+1][(i*H+j-1)*2] = 1; 56 edge[(i*H+j)*2+1].push_back((i*H+j-1)*2); 57 58 cap[(i*H+j)*2+1][(i*H+j+1)*2] = 1; 59 edge[(i*H+j)*2+1].push_back((i*H+j+1)*2); 60 61 if (i == 1 || j == 1 || i == H || j == W) { 62 cap[(i*H+j)*2+1][T] = 1; 63 edge[(i*H+j)*2+1].push_back(T); 64 } 65 } 66 } 67 } 68 int MaxFlow(int S, int T, int H, int W) 69 { 70 int result = 0; 71 while (1) { 72 memset(bottleneck, 0, sizeof(bottleneck)); 73 bottleneck[S] = INF; 74 queue<int> Q; 75 Q.push(S); 76 77 while (!Q.empty() && !bottleneck[T]) { 78 int now = Q.front(); 79 Q.pop(); 80 for (int nxt : edge[now]) { 81 if (!bottleneck[nxt] && cap[now][nxt] > flow[now][nxt]) { 82 Q.push(nxt); 83 pre[nxt] = now; 84 bottleneck[nxt] = min(bottleneck[now], cap[now][nxt] - flow[now][nxt]); 85 } 86 } 87 } 88 if (bottleneck[T] == 0) break; 89 90 for (int now = T; now != S; now = pre[now]) { 91 flow[pre[now]][now] += bottleneck[T]; 92 flow[now][pre[now]] -= bottleneck[T]; 93 } 94 result += bottleneck[T]; 95 } 96 return result; 97 }
uva,259
二分图最大匹配,匹配到就输出对应的任务数,不能匹配输出'_' 因为任务要全被做完,且一台电脑只能做一个任务且要做一天,电脑最多10台当任务大于10直接不用考虑。
然后就是打印输出,这个想不出来,参考了别人的。主要是今天状态不太好,然后就没去想。。。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <vector> 5 #include <string> 6 #include <cstring> 7 using namespace std; 8 9 int match[10]; 10 int cc[0xff]; 11 int vis[10]; 12 vector<char> G[0xff]; 13 14 int dfs(int u) 15 { 16 for(vector<char>::iterator it=G[u].begin();it!=G[u].end();it++) 17 { 18 int v=*it-'0'; 19 if(!vis[v]) 20 { 21 vis[v]=1; 22 if(match[v]==-1 || dfs(match[v])) 23 { 24 match[v]=u; 25 return 1; 26 } 27 } 28 } 29 return 0; 30 } 31 32 int main() 33 { 34 while(cin.peek()!=EOF) 35 { 36 int index=0; 37 string line; 38 while(getline(cin,line),isalpha(line[0])) 39 { 40 for(char i='0';i<line[1];i++) 41 { 42 G[index].clear(); 43 cc[index]=line[0]; 44 for(int j=3;line[j]!=';';j++) G[index].push_back(line[j]); 45 ++index; 46 } 47 } 48 49 if(index<=10) 50 { 51 int cnt=0; 52 memset(match,-1,sizeof(match)); 53 for(int i=0;i<index;i++) 54 { 55 memset(vis,0,sizeof(vis)); 56 cnt+=dfs(i); 57 } 58 59 if(cnt!=index) puts("!"); 60 else 61 { 62 for(int i=0;i<10;i++) 63 { 64 if(match[i]==-1) putchar('_'); 65 else 66 putchar(cc[match[i]]); 67 } 68 putchar('\n'); 69 } 70 } 71 else 72 puts("!"); 73 } 74 return 0; 75 }
uva,11380
有容量限制,即存在某个点只允许几个,但是如果这个点允许无数个通过即为无容量限制,对于无容量限制的不需要拆点。
拆点之后跑一次dinic算法,轻松愉快。泰坦尼克号,我就是一炮。
讲一下技巧:这个代码 = =真的简单易懂工整膜拜---
首先只需要对'.'进行拆点,然后超级源点流向'*', '#'流向超级汇点,'.'拆之后流出的点由拆点流出,
s向所有的'*'连一条边,容量为1,所有的'#'向汇点连一条边容量为p,'.'向拆点连一条边容量为1,拆点'.'向任何有意义的点连一条边容量为1,其余的容量为inf(orz我这一点没想到。。。),然后就是拆点的技巧= =没有想得那么完善= =至于跑算法也没有充分考虑。我感觉ek应该也是可以的,我尝试一下。
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <cctype> 5 #include <cmath> 6 #include <cstdlib> 7 #include <cstring> 8 #include <string> 9 #include <set> 10 #include <vector> 11 12 using namespace std; 13 14 const int N=1e5+10,INF=0x3f3f3f3f; 15 const int M=1e6+10; 16 17 int head[N],pnt[N],cap[M],nxt[M],cnt; 18 19 void add_edge(int u,int v,int w) 20 { 21 pnt[cnt]=v; 22 cap[cnt]=w; 23 nxt[cnt]=head[u]; 24 head[u]=cnt++; 25 } 26 27 void add_double(int u,int v,int w1,int w2=0) 28 { 29 add_edge(u,v,w1); 30 add_edge(v,u,w2); 31 } 32 33 int lev[N],cur[N]; 34 35 bool bfs(int s,int t)//dinic算法效率高 36 { 37 queue<int> q; 38 memset(lev,0,sizeof(lev)); 39 q.push(s);lev[s]=1; 40 while(q.size() && !lev[t]) 41 { 42 int u=q.front();q.pop(); 43 for(int i=head[u];~i;i=nxt[i]) 44 { 45 int v=pnt[i]; 46 if(cap[i]>0 && !lev[v]) 47 { 48 lev[v]=lev[u]+1; 49 q.push(v); 50 } 51 } 52 } 53 return lev[t]; 54 } 55 56 int dfs(int u,int t,int delta) 57 { 58 if(u==t || !delta) return delta; 59 60 int ret=0; 61 for(int i=cur[u];~i;i=nxt[i]) 62 { 63 int v=pnt[i]; 64 if(cap[i]>0 && lev[v]==lev[u]+1) 65 { 66 int d=dfs(v,t,min(delta,cap[i])); 67 cur[u]=i; 68 ret+=d;delta-=d; 69 cap[i]-=d; 70 cap[i^1]+=d; 71 72 if(delta==0) return ret; 73 } 74 } 75 lev[u]=0; 76 return ret; 77 } 78 79 int dinic(int s,int t) 80 { 81 int ret=0; 82 while(bfs(s,t)) 83 { 84 for(int i=s;i<=t;i++) 85 cur[i]=head[i]; 86 ret+=dfs(s,t,INF); 87 } 88 return ret; 89 } 90 91 int n,m,p; 92 char a[105][105]; 93 94 int get(int x,int y) 95 { 96 return m*(x-1)+y; 97 } 98 99 int d[][2]={-1,0,0,-1,1,0,0,1}; 100 101 bool isIllegal(int x,int y) 102 { 103 return x<1 || x>n || y<1 || y>m; 104 } 105 106 int main() 107 { 108 while(~scanf("%d%d%d",&n,&m,&p)) 109 { 110 for(int i=1;i<=n;i++) scanf("%s",a[i]+1); 111 112 cnt=0;memset(head,-1,sizeof(head)); 113 114 int s=0,t=2*m*n+1,OFF=n*m; 115 for(int i=1;i<=n;i++) 116 for(int j=1;j<=m;j++) 117 { 118 if(a[i][j]=='~') continue; 119 if(a[i][j]=='*') add_double(s,get(i,j),1); 120 if(a[i][j]=='#') add_double(get(i,j),t,p); 121 if(a[i][j]=='.') add_double(get(i,j),get(i,j)+OFF,1); 122 for(int k=0;k<4;++k) 123 { 124 int nx=i+d[k][0],ny=j+d[k][1]; 125 if(isIllegal(nx,ny) || a[nx][ny]=='*' || a[nx][ny]=='~') continue; 126 //因为只有'.'需要拆,拆之后由原来点流出的现在由拆点流出。 127 if(a[i][j]=='.') add_double(get(i,j)+OFF,get(nx,ny),1); 128 //'@'流向任何为无穷,'#'只流向汇点,其他都为inf 129 else add_double (get(i,j),get(nx,ny),INF); 130 } 131 } 132 printf("%d\n",dinic(s,t)); 133 } 134 return 0; 135 }

浙公网安备 33010602011771号