网络流24题做题日记
今天开始做网络流,只能说24题道道经典,虽然才做几道,做一道添一道吧
第一题 飞行员配对方案问题
第二次世界大战时期, 英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员, 其中1 名是英国飞行员,另1名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。 如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。 对于给定的外籍飞行员与英国飞行员的配合情况,编程求出皇家空军一次能派出最多的飞机数。
此题是二分图匹配问题,将每一对飞行员连一条边,容量为+∞,源点连到外籍飞行员容量1的边,英国飞行员连到汇点一条容量为1的边。
1 #include<iostream> 2 #include<vector> 3 #include<cstring> 4 #include<queue> 5 #include<cstdio> 6 using namespace std; 7 int n,m; 8 int ans; 9 struct node 10 { 11 int to; 12 int val; 13 int opp; 14 }; 15 vector <node> v[105]; 16 int d[105]; 17 void add(int x,int y,int val) 18 { 19 node tmp; 20 tmp.to=y; 21 tmp.val=val; 22 tmp.opp=v[y].size(); 23 v[x].push_back(tmp); 24 tmp.to=x; 25 tmp.val=0; 26 tmp.opp=v[x].size()-1; 27 v[y].push_back(tmp); 28 } 29 bool make_level() 30 { 31 queue<int>q; 32 for(int i=0;i<=n+1;i++)d[i]=-1; 33 d[0]=0; 34 q.push(0); 35 while(!q.empty()) 36 { 37 int x=q.front(); 38 q.pop(); 39 for(int i=0;i<v[x].size();i++) 40 { 41 if(d[v[x][i].to]==-1 && v[x][i].val>0) 42 { 43 d[v[x][i].to]=d[x]+1; 44 q.push(v[x][i].to); 45 } 46 } 47 } 48 return d[n+1]!=-1; 49 } 50 int extend(int x,int cap) 51 { 52 if(x==n+1)return cap; 53 int r=0; 54 for(int i=0;i<v[x].size();i++) 55 { 56 if(v[x][i].val>0 && d[v[x][i].to]==d[x]+1) 57 { 58 int what=min(v[x][i].val,cap-r); 59 what=extend(v[x][i].to,what); 60 r+=what; 61 v[x][i].val-=what; 62 v[v[x][i].to][v[x][i].opp].val+=what; 63 } 64 } 65 if(!r)d[x]=-1; 66 return r; 67 } 68 69 int main() 70 { 71 scanf("%d%d",&m,&n); 72 int x,y; 73 scanf("%d%d",&x,&y); 74 while(x!=-1 && y!=-1) 75 { 76 if(x>y)swap(x,y); 77 add(x,y,19991231); 78 scanf("%d%d",&x,&y); 79 } 80 for(int i=1;i<=m;i++) 81 add(0,i,1); 82 for(int i=m+1;i<=n;i++) 83 add(i,n+1,1); 84 while(make_level()) 85 { 86 ans+=extend(0,19991231); 87 } 88 printf("%d\n",ans); 89 return 0; 90 }
第二题 太空飞行计划问题
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1, I2,…In}。 实验 Ej需要用到的仪器是 I的子集 RjÍI。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。 对于给定的实验和仪器配置情况,编程求出最大的净收益。
最大权闭合子图。最大流最小割定理。
由源点连一条容量为收益的边到每一个实验,从每个仪器连一条容量为费用的边到汇点,在从每个实验连多条容量为无穷大的边到对应的仪器,求最小割,转换为最大流问题。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 int n,m; 8 const int inf=19991231; 9 int s=0,t; 10 bool flag; 11 int ans; 12 char tmp[10005]; 13 int res; 14 struct node 15 { 16 int to; 17 int val; 18 int opp; 19 }; 20 vector <node> v[2005]; 21 int d[2005]; 22 void add(int x,int y,int vv) 23 { 24 node tmp; 25 tmp.to=y; 26 tmp.val=vv; 27 tmp.opp=v[y].size(); 28 v[x].push_back(tmp); 29 tmp.to=x; 30 tmp.val=0;//边要是0 31 tmp.opp=v[x].size()-1; 32 v[y].push_back(tmp); 33 } 34 35 bool make_level() 36 { 37 queue<int>q; 38 for(int i=0;i<=t;i++)d[i]=-1; 39 d[0]=0; 40 q.push(0); 41 while(!q.empty()) 42 { 43 int x=q.front(); 44 q.pop(); 45 for(int i=0;i<v[x].size();i++) 46 { 47 if(d[v[x][i].to]==-1 && v[x][i].val>0) 48 { 49 d[v[x][i].to]=d[x]+1; 50 q.push(v[x][i].to); 51 } 52 } 53 } 54 return (d[t]!=-1); 55 } 56 int extend(int x,int cap) 57 { 58 if(x==t)return cap; 59 int r=0; 60 for(int i=0;i<v[x].size();i++) 61 { 62 if(v[x][i].val>0 && d[v[x][i].to]==d[x]+1) 63 { 64 int what=min(v[x][i].val,cap-r); 65 what=extend(v[x][i].to,what); 66 r+=what; 67 v[x][i].val-=what; 68 v[v[x][i].to][v[x][i].opp].val+=what; 69 } 70 } 71 if(!r)d[x]=-2; 72 return r; 73 } 74 int main() 75 { 76 scanf("%d%d",&n,&m); 77 t=m+n+1; 78 for(int i=1;i<=n;i++) 79 { 80 int cost; 81 scanf("%d", &cost); 82 ans+=cost; 83 add(0,i,cost); 84 int u; 85 while (getchar() != '\n') { 86 scanf("%d", &u); 87 add(i, u+n, inf); 88 } 89 } 90 for(int i=1;i<=m;i++) 91 { 92 int x; 93 scanf("%d",&x); 94 add(i+n,t,x); 95 } 96 while(make_level()) 97 { 98 res+=extend(0,19991231); 99 } 100 printf("%d\n",ans-res); 101 return 0; 102 }
第三题 最小路径覆盖问题
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个顶点恰好在 P 的一条路上,则称 P是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。 对于给定的给定有向无环图G,编程找出最少需要几条路可以覆盖图G。
将每个点拆成两个点,xi,yi,对于每条边连一条xi,yj的边,求二分图匹配。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<vector> 5 #define inf 19991231 6 using namespace std; 7 int n,m; 8 struct node 9 { 10 int to; 11 int val; 12 int opp; 13 }; 14 vector <node> v[505]; 15 int s=0; 16 int t; 17 int ans=0; 18 int d[505]; 19 void add(int x,int y,int val) 20 { 21 node tmp; 22 tmp.to=y; 23 tmp.val=val; 24 tmp.opp=v[y].size(); 25 v[x].push_back(tmp); 26 tmp.to=x; 27 tmp.val=0; 28 tmp.opp=v[x].size()-1; 29 v[y].push_back(tmp); 30 } 31 bool make_level() 32 { 33 queue<int> q; 34 for(int i=1;i<=t;i++)d[i]=-1; 35 d[0]=0; 36 q.push(0); 37 while(!q.empty()) 38 { 39 int x=q.front(); 40 q.pop(); 41 for(int i=0;i<v[x].size();i++) 42 { 43 if(d[v[x][i].to]==-1 && v[x][i].val>0) 44 { 45 d[v[x][i].to]=d[x]+1; 46 q.push(v[x][i].to); 47 } 48 } 49 } 50 return d[t]!=-1; 51 } 52 int dfs(int x,int cap) 53 { 54 int r=0; 55 if(x==t)return cap; 56 for(int i=0;i<v[x].size();i++) 57 { 58 if(v[x][i].val>0 && d[v[x][i].to]==d[x]+1) 59 { 60 int what=min(v[x][i].val,cap-r); 61 what=dfs(v[x][i].to,what); 62 r+=what; 63 v[x][i].val-=what; 64 v[v[x][i].to][v[x][i].opp].val+=what; 65 } 66 } 67 if(!r)d[x]=-2; 68 return r; 69 } 70 int main() 71 { 72 scanf("%d%d",&n,&m); 73 t=2*n+1; 74 for(int i=1;i<=n;i++) 75 { 76 add(s,i,1); 77 add(i+n,t,1); 78 } 79 for(int i=1;i<=m;i++) 80 { 81 int x,y; 82 scanf("%d%d",&x,&y); 83 add(x,y+n,inf); 84 } 85 while(make_level()) 86 { 87 ans+=dfs(0,inf); 88 } 89 printf("%d\n",n-ans); 90 return 0; 91 }
第四题 魔术球问题
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,。。。的球。 (1)每次只能在某根柱子的最上面放球。 (2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。 试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11个球。 对于给定的n,计算在 n根柱子上最多能放多少个球。
二分枚举答案,判断最小路径覆盖是否小于等于n,转换很重要。
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<queue> 5 #include<cmath> 6 #define inf 19991231 7 using namespace std; 8 int N; 9 int s,t; 10 struct node 11 { 12 int to; 13 int val; 14 int opp; 15 }; 16 vector <node> v[4005]; 17 int d[4005]; 18 void add(int x,int y,int val) 19 { 20 node tmp; 21 tmp.to=y; 22 tmp.val=val; 23 tmp.opp=v[y].size(); 24 v[x].push_back(tmp); 25 tmp.to=x; 26 tmp.val=0; 27 tmp.opp=v[x].size()-1; 28 v[y].push_back(tmp); 29 } 30 bool make_level() 31 { 32 queue<int> q; 33 for(int i=1;i<=t;i++)d[i]=-1; 34 d[0]=0; 35 q.push(0); 36 while(!q.empty()) 37 { 38 int x=q.front(); 39 q.pop(); 40 for(int i=0;i<v[x].size();i++) 41 { 42 if(d[v[x][i].to]==-1 && v[x][i].val>0) 43 { 44 d[v[x][i].to]=d[x]+1; 45 q.push(v[x][i].to); 46 } 47 } 48 } 49 return d[t]!=-1; 50 } 51 int dfs(int x,int cap) 52 { 53 int r=0; 54 if(x==t)return cap; 55 for(int i=0;i<v[x].size();i++) 56 { 57 if(v[x][i].val>0 && d[v[x][i].to]==d[x]+1) 58 { 59 int what=min(v[x][i].val,cap-r); 60 what=dfs(v[x][i].to,what); 61 r+=what; 62 v[x][i].val-=what; 63 v[v[x][i].to][v[x][i].opp].val+=what; 64 } 65 } 66 if(!r)d[x]=-2; 67 return r; 68 } 69 int judge(int x) 70 { 71 s=0; 72 t=x*2+1; 73 for(int i=0;i<=t;i++) 74 v[i].clear(); 75 for(int i=1;i<=x;i++)add(s,i,1); 76 for(int i=1;i<=x;i++) 77 { 78 for(int j=i+1;j<=x;j++) 79 { 80 int t=sqrt(i+j); 81 if(t*t==i+j) 82 { 83 add(i,j+x,inf); 84 } 85 } 86 } 87 for(int i=1;i<=x;i++)add(i+x,t,1); 88 int ans=0; 89 while(make_level()) 90 { 91 ans+=dfs(s,inf); 92 } 93 return x-ans; 94 } 95 int main(){ 96 scanf("%d",&N); 97 int l=1,r=1575; 98 while(l<=r) 99 { 100 int mid=(l+r)/2; 101 if(judge(mid)<=N) 102 { 103 l=mid+1; 104 } 105 else r=mid-1; 106 } 107 printf("%d\n",r); 108 return 0; 109 }
第五题 圆桌问题
假设有来自 m 个不同单位的代表参加一次国际会议。会议餐厅共有 m张餐桌,为了使代表们充分交流, 希望从同一个单位来的代表不在同一个餐桌就餐。 试设计一个算法,求出最多可以让多少人有位置可坐。
超级源点连向代表队,容量为人数,桌子连向超级汇点,容量为可坐人数,每个代表队向每个桌子连边,容量为1。跑最大流。
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<queue> 5 #define inf 19991231 6 using namespace std; 7 int n,m; 8 int s,t; 9 struct node 10 { 11 int val; 12 int opp; 13 int to; 14 }; 15 vector <node> v[450]; 16 int d[460]; 17 void add(int x,int y,int val) 18 { 19 node tmp; 20 tmp.to=y;tmp.val=val;tmp.opp=v[y].size(); 21 v[x].push_back(tmp); 22 tmp.to=x;tmp.val=0;tmp.opp=v[x].size()-1; 23 v[y].push_back(tmp); 24 } 25 bool make_level() 26 { 27 for(int i=1;i<=t;i++)d[i]=-1; 28 d[0]=0; 29 queue <int> q; 30 q.push(0); 31 while(!q.empty()) 32 { 33 int x=q.front(); 34 q.pop(); 35 for(int i=0;i<v[x].size();i++) 36 { 37 if(d[v[x][i].to]==-1 && v[x][i].val>0) 38 { 39 d[v[x][i].to]=d[x]+1; 40 q.push(v[x][i].to); 41 } 42 } 43 } 44 return d[t]!=-1; 45 } 46 int dfs(int x,int cap) 47 { 48 if(x==t)return cap; 49 int r=0; 50 for(int i=0;i<v[x].size();i++) 51 { 52 if(d[v[x][i].to]==d[x]+1 && v[x][i].val>0) 53 { 54 int what=min(v[x][i].val,cap-r); 55 what=dfs(v[x][i].to,what); 56 r+=what; 57 v[x][i].val-=what; 58 v[v[x][i].to][v[x][i].opp].val+=what; 59 } 60 } 61 if(!r)d[x]=-2; 62 return r; 63 } 64 int ans=0; 65 int main() 66 { 67 scanf("%d%d",&n,&m); 68 s=0;t=n+m+1; 69 for(int i=1;i<=n;i++) 70 { 71 for(int j=1;j<=m;j++) 72 add(i,j+n,1); 73 } 74 for(int i=1;i<=n;i++) 75 { 76 int x; 77 scanf("%d",&x); 78 add(s,i,x); 79 } 80 for(int i=1;i<=m;i++) 81 { 82 int x; 83 scanf("%d",&x); 84 add(i+n,t,x); 85 } 86 while(make_level()) 87 { 88 ans+=dfs(0,inf); 89 } 90 printf("%d\n",ans); 91 return 0; 92 }
第六题 最长递增子序列问题
给定正整数序列 (1)计算其最长递增子序列的长度s。 (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。 (3)如果允许在取出的序列中多次使用x1和 xn,则从给定序列中最多可取出多少个长度为s的递增子序列。 设计有效算法完成(1)(2)(3)提出的计算任务。
dpf(i)为以i开头最长的点,每个点分为两个点,ia,ib,ia连向ib容量为1边,源点连向f(i)的ia为s的边,f(i)为1的ib点连向汇点容量为1的点,若f[i]==f[j]+1 and a[i]<a[j],则连ib到ja容量为1的边。跑最大流。我们可以感性发现,这样流过的流量长度恰好为n。
第三问只需将关于1,n的容量变为无穷大即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<vector> 5 #define inf 19991231 6 using namespace std; 7 int n; 8 int s,t; 9 int ans=0; 10 int res; 11 int f[400]; 12 int a[400]; 13 struct node 14 { 15 int val; 16 int opp; 17 int to; 18 }; 19 vector <node> v[1000]; 20 int d[1000]; 21 void add(int x,int y,int val) 22 { 23 node tmp; 24 tmp.to=y;tmp.val=val;tmp.opp=v[y].size(); 25 v[x].push_back(tmp); 26 tmp.to=x;tmp.val=0;tmp.opp=v[x].size()-1; 27 v[y].push_back(tmp); 28 } 29 bool make_level() 30 { 31 for(int i=1;i<=t;i++)d[i]=-1; 32 d[0]=0; 33 queue <int> q; 34 q.push(0); 35 while(!q.empty()) 36 { 37 int x=q.front(); 38 q.pop(); 39 for(int i=0;i<v[x].size();i++) 40 { 41 if(d[v[x][i].to]==-1 && v[x][i].val>0) 42 { 43 d[v[x][i].to]=d[x]+1; 44 q.push(v[x][i].to); 45 } 46 } 47 } 48 return d[t]!=-1; 49 } 50 int dfs(int x,int cap) 51 { 52 if(x==t)return cap; 53 int r=0; 54 for(int i=0;i<v[x].size();i++) 55 { 56 if(d[v[x][i].to]==d[x]+1 && v[x][i].val>0) 57 { 58 int what=min(v[x][i].val,cap-r); 59 what=dfs(v[x][i].to,what); 60 r+=what; 61 v[x][i].val-=what; 62 v[v[x][i].to][v[x][i].opp].val+=what; 63 } 64 } 65 if(!r)d[x]=-2; 66 return r; 67 } 68 int main() 69 { 70 scanf("%d",&n); 71 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 72 for(int i=n;i>=1;i--) 73 { 74 f[i]=1; 75 for(int j=i+1;j<=n;j++) 76 { 77 if(a[j]>a[i] && f[j]+1>f[i])f[i]=f[j]+1; 78 } 79 if(f[i]>res)res=f[i]; 80 } 81 printf("%d\n",res); 82 s=0;t=n*2+1; 83 for(int i=1;i<=n;i++) 84 { 85 add(i,i+n,1); 86 if(f[i]==res) 87 { 88 add(s,i,1); 89 } 90 if(f[i]==1) 91 { 92 add(i+n,t,1); 93 } 94 } 95 for(int i=1;i<=n;i++) 96 { 97 for(int j=i+1;j<=n;j++) 98 { 99 if(f[i]==f[j]+1 && a[i]<a[j]) 100 add(i+n,j,1); 101 } 102 } 103 while(make_level()) 104 { 105 ans+=dfs(s,inf); 106 } 107 printf("%d\n",ans); 108 res=ans; 109 for(int i=0;i<v[s].size();i++) 110 if(v[s][i].to==1)v[s][i].val=inf; 111 for(int i=0;i<v[1].size();i++) 112 if(v[1][i].to==1+n)v[1][i].val=inf; 113 for(int i=0;i<v[n].size();i++) 114 if(v[n][i].to==n+n)v[n][i].val=inf; 115 for(int i=0;i<v[n+n].size();i++) 116 if(v[n+n][i].to==t)v[n+n][i].val=inf; 117 int ans=0; 118 while(make_level()) 119 { 120 ans+=dfs(s,inf); 121 } 122 printf("%d\n",ans+res); 123 return 0; 124 }
第七题 试题库问题
觉得生活了无生趣的TTLJ准备出一套题目虐下机房众神牛,但是一套题目4个题目根本难不住大家,极度阴暗的TTLJ想让题目越多越好,当然一套题目全是一种类型会让生活更加无趣,于是乎这套题目里会有很多类型,当然每种类型的题目数量有个上限。 阅水题无数的TTLJ准备用他做过的水题来组成这套题目,当然有时候水题也可能是很多类型的揉合在一起的综合题,在组成试卷的时候这种题目会被认为只有一种类型。 下面请问TTLJ这套题目最多有多少题目呢?
此题和圆桌问题类似,只是将每道题连到汇点的容量变为1
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<queue> 5 #define inf 19991231 6 using namespace std; 7 int n,m; 8 int s,t; 9 struct node 10 { 11 int val; 12 int opp; 13 int to; 14 }; 15 vector <node> v[2000]; 16 int d[2000]; 17 void add(int x,int y,int val) 18 { 19 node tmp; 20 tmp.to=y;tmp.val=val;tmp.opp=v[y].size(); 21 v[x].push_back(tmp); 22 tmp.to=x;tmp.val=0;tmp.opp=v[x].size()-1; 23 v[y].push_back(tmp); 24 } 25 bool make_level() 26 { 27 for(int i=1;i<=t;i++)d[i]=-1; 28 d[0]=0; 29 queue <int> q; 30 q.push(0); 31 while(!q.empty()) 32 { 33 int x=q.front(); 34 q.pop(); 35 for(int i=0;i<v[x].size();i++) 36 { 37 if(d[v[x][i].to]==-1 && v[x][i].val>0) 38 { 39 d[v[x][i].to]=d[x]+1; 40 q.push(v[x][i].to); 41 } 42 } 43 } 44 return d[t]!=-1; 45 } 46 int dfs(int x,int cap) 47 { 48 if(x==t)return cap; 49 int r=0; 50 for(int i=0;i<v[x].size();i++) 51 { 52 if(d[v[x][i].to]==d[x]+1 && v[x][i].val>0) 53 { 54 int what=min(v[x][i].val,cap-r); 55 what=dfs(v[x][i].to,what); 56 r+=what; 57 v[x][i].val-=what; 58 v[v[x][i].to][v[x][i].opp].val+=what; 59 } 60 } 61 if(!r)d[x]=-2; 62 return r; 63 } 64 int ans=0; 65 int main() 66 { 67 scanf("%d%d",&n,&m); 68 t=n+m+1; 69 s=0; 70 for(int i=1;i<=n;i++) 71 { 72 int x; 73 scanf("%d",&x); 74 add(s,i,x); 75 } 76 for(int i=1;i<=m;i++) 77 { 78 add(i+n,t,1); 79 int tt; 80 scanf("%d",&tt); 81 for(int j=0;j<tt;j++) 82 { 83 int x; 84 scanf("%d",&x); 85 // cout<<x<<endl; 86 add(x,i+n,1); 87 } 88 } 89 90 while(make_level()) 91 { 92 ans+=dfs(0,inf); 93 } 94 printf("%d\n",ans); 95 return 0; 96 }
第八题 暂跳过(我太弱。。。)
第九题 方格取数问题
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。 对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
此题先对格子黑白染色,s向黑点连边,白点向t连边,容量为值,每个黑格子与相邻白格子连无穷大的边
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<queue> 5 #define inf 1<<30 6 using namespace std; 7 int n,m; 8 int a[35][35]; 9 int sum; 10 int s,t; 11 struct node 12 { 13 int val; 14 int opp; 15 int to; 16 }; 17 vector <node> v[2000]; 18 int d[2000]; 19 void add(int x,int y,int val) 20 { 21 // cout<<x<<" "<<y<<endl; 22 node tmp; 23 tmp.to=y;tmp.val=val;tmp.opp=v[y].size(); 24 v[x].push_back(tmp); 25 tmp.to=x;tmp.val=0;tmp.opp=v[x].size()-1; 26 v[y].push_back(tmp); 27 } 28 bool make_level() 29 { 30 for(int i=1;i<=t;i++)d[i]=-1; 31 d[0]=0; 32 queue <int> q; 33 q.push(0); 34 while(!q.empty()) 35 { 36 int x=q.front(); 37 q.pop(); 38 for(int i=0;i<v[x].size();i++) 39 { 40 if(d[v[x][i].to]==-1 && v[x][i].val>0) 41 { 42 d[v[x][i].to]=d[x]+1; 43 q.push(v[x][i].to); 44 } 45 } 46 } 47 return d[t]!=-1; 48 } 49 int dfs(int x,int cap) 50 { 51 if(x==t)return cap; 52 int r=0; 53 for(int i=0;i<v[x].size();i++) 54 { 55 if(d[v[x][i].to]==d[x]+1 && v[x][i].val>0) 56 { 57 int what=min(v[x][i].val,cap-r); 58 what=dfs(v[x][i].to,what); 59 r+=what; 60 v[x][i].val-=what; 61 v[v[x][i].to][v[x][i].opp].val+=what; 62 } 63 } 64 if(!r)d[x]=-2; 65 return r; 66 } 67 int ans=0; 68 int main() 69 { 70 // freopen("1404.out","w",stdout); 71 scanf("%d%d",&n,&m); 72 t=n*m+1;s=0; 73 for(int i=1;i<=n;i++) 74 { 75 for(int j=1;j<=m;j++) 76 { 77 scanf("%d",&a[i][j]); 78 sum+=a[i][j]; 79 } 80 } 81 for(int i=1;i<=n;i++) 82 { 83 for(int j=1;j<=m;j++) 84 { 85 int p=(i-1)*m+j; 86 if((i+j)&1) 87 { 88 add(p,t,a[i][j]); 89 } 90 else 91 { 92 add(s,p,a[i][j]); 93 if(j<m)add(p,p+1,inf); 94 if(i<n)add(p,p+m,inf); 95 if(j>1)add(p,p-1,inf); 96 if(i>1)add(p,p-m,inf); 97 } 98 99 } 100 } 101 while(make_level()) 102 { 103 ans+=dfs(0,inf); 104 // cout<<ans<<endl; 105 } 106 printf("%d\n",sum-ans); 107 return 0; 108 }
第十题 餐巾纸问题
一个餐厅在相继的N 天里, 每天需用的餐巾数不尽相同。 假设第i天需要ri块餐巾(i=1,2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为p分;或者把旧餐巾送到快洗部,洗一块需 m天,其费用为 f 分;或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s
费用流第一题,建图是参考别人的,每个点拆成两个点,Ai,Bi,
add(s,i,x,0);
add(i+N,t,x,0);
add(s,i+N,inf,p);
add(i,i+1,inf,0);
add(i,i+N+m,inf,f);
add(i,i+N+n,inf,s);
其实建图就是把每个条件加入图中,要多会转换问题。
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<queue> 5 #define inf 19991231 6 using namespace std; 7 struct node 8 { 9 int to; 10 int cap; 11 int cost; 12 int opp; 13 }; 14 vector <node> v[3005]; 15 bool in[3005]; 16 int wh[3005]; 17 int d[3005]; 18 int pre[3005]; 19 int s,t; 20 void add(int x,int y,int cap,int cost) 21 { 22 node tmp; 23 tmp.to=y;tmp.cap=cap;tmp.cost=cost;tmp.opp=v[y].size(); 24 v[x].push_back(tmp); 25 tmp.to=x;tmp.cap=0;tmp.cost=-cost;tmp.opp=v[x].size()-1; 26 v[y].push_back(tmp); 27 } 28 int N,p,m,f,n,ss; 29 bool spfa() 30 { 31 queue<int >q; 32 for(int i=1;i<=t;i++)d[i]=inf; 33 in[s]=true; 34 d[s]=0; 35 q.push(s); 36 while(!q.empty()) 37 { 38 int x=q.front(); 39 in[x]=false; 40 q.pop(); 41 for(int i=0;i<v[x].size();i++) 42 { 43 if(v[x][i].cap>0 && d[v[x][i].to]>d[x]+v[x][i].cost) 44 { 45 d[v[x][i].to]=d[x]+v[x][i].cost; 46 pre[v[x][i].to]=x; 47 wh[v[x][i].to]=i; 48 if(!in[v[x][i].to]) 49 { 50 in[v[x][i].to]=true; 51 q.push(v[x][i].to); 52 } 53 } 54 } 55 } 56 return d[t]<inf; 57 } 58 int min_flow() 59 { 60 int ans=0; 61 while(spfa()) 62 { 63 int flow=inf; 64 for(int i=t;i!=s;) 65 { 66 flow=min(flow,v[pre[i]][wh[i]].cap); 67 i=pre[i]; 68 } 69 for(int i=t;i!=s;) 70 { 71 ans+=v[pre[i]][wh[i]].cost*flow; 72 v[pre[i]][wh[i]].cap-=flow; 73 v[i][v[pre[i]][wh[i]].opp].cap+=flow; 74 i=pre[i]; 75 } 76 } 77 return ans; 78 } 79 int main() 80 { 81 scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&ss); 82 s=0;t=N*2+1; 83 for(int i=1;i<=N;i++) 84 { 85 int x; 86 scanf("%d",&x); 87 add(s,i,x,0); 88 add(i+N,t,x,0); 89 add(s,i+N,inf,p); 90 if(i<N)add(i,i+1,inf,0); 91 if(i+m<=N)add(i,i+N+m,inf,f); 92 if(i+n<=N)add(i,i+N+n,inf,ss); 93 } 94 printf("%d\n",min_flow()); 95 return 0; 96 }
第十一题 航空城路线问题
给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。 (1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。 (2)除起点城市外,任何城市只能访问1次。 对于给定的航空图,试设计一个算法求出一条路线最多可以经过几个城市。
思路:(转发自别人博客)
1. 题目最终可以抽象为:最长不相交路径问题,两条从 s 到 t 的不相交路径的最大长度;
2. 把每个点分成 <i, X> <i, Y> 两点,引弧,容量为 1,费用为 1,特殊的:s, t 容量设置为 2,因为可以重复选择;
3. 对于 i, j 存在路径且 i < j 则 <i, Y> 向 <j, X> 引弧,容量为 1,费用为 0. 特殊的如果 i = s, j = t,容量设置为 2;
4. 求上述网络的最大费用最大流,把费用设置为负数,即可求最小费用最大流。结果 - 2 即是输出结果。
1 #include<iostream> 2 #include<map> 3 #include<string> 4 #include<cstdio> 5 #include<vector> 6 #include<queue> 7 #define inf 19991231; 8 using namespace std; 9 int n,m; 10 int s,t; 11 string tmp; 12 map<string,int >M; 13 struct node 14 { 15 int to; 16 int cap; 17 int cost; 18 int opp; 19 }; 20 vector <node> v[3005]; 21 bool in[3005]; 22 int wh[3005]; 23 int d[3005]; 24 int pre[3005]; 25 void add(int x,int y,int cap,int cost) 26 { 27 node tmp; 28 tmp.to=y;tmp.cap=cap;tmp.cost=cost;tmp.opp=v[y].size(); 29 v[x].push_back(tmp); 30 tmp.to=x;tmp.cap=0;tmp.cost=-cost;tmp.opp=v[x].size()-1; 31 v[y].push_back(tmp); 32 } 33 bool spfa() 34 { 35 queue<int >q; 36 for(int i=2;i<=t;i++){ 37 d[i]=inf; 38 in[i]=false; 39 } 40 in[s]=true; 41 d[s]=0; 42 q.push(s); 43 while(!q.empty()) 44 { 45 int x=q.front(); 46 in[x]=false; 47 q.pop(); 48 for(int i=0;i<v[x].size();i++) 49 { 50 if(v[x][i].cap>0 && d[v[x][i].to]>d[x]+v[x][i].cost) 51 { 52 d[v[x][i].to]=d[x]+v[x][i].cost; 53 pre[v[x][i].to]=x; 54 wh[v[x][i].to]=i; 55 if(!in[v[x][i].to]) 56 { 57 in[v[x][i].to]=true; 58 q.push(v[x][i].to); 59 } 60 } 61 } 62 } 63 return d[t]<inf; 64 } 65 int min_cost() 66 { 67 int ans=0; 68 while(spfa()) 69 { 70 int flow=inf; 71 for(int i=t;i!=s;) 72 { 73 flow=min(flow,v[pre[i]][wh[i]].cap); 74 i=pre[i]; 75 } 76 for(int i=t;i!=s;) 77 { 78 ans+=v[pre[i]][wh[i]].cost*flow; 79 v[pre[i]][wh[i]].cap-=flow; 80 v[i][v[pre[i]][wh[i]].opp].cap+=flow; 81 i=pre[i]; 82 } 83 } 84 return ans; 85 } 86 int main() 87 { 88 scanf("%d%d",&n,&m); 89 s=1;t=n*2; 90 for(int i=1;i<=n;i++) 91 { 92 cin>>tmp; 93 M[tmp]=i; 94 if(i!=1 && i!=n)add(i,i+n,1,-1); 95 else add(i,i+n,2,-1); 96 } 97 for(int i=1;i<=m;i++) 98 { 99 string sx,sy; 100 cin>>sx>>sy; 101 int x=min(M[sx],M[sy]); 102 int y=max(M[sx],M[sy]); 103 if(x!=1 && y!=n) 104 add(x+n,y,1,0); 105 else add(x+n,y,2,0); 106 } 107 int t=-min_cost()-2; 108 if(t>=0)printf("%d\n",t);else puts("No Solution!"); 109 return 0;