标签:
网络流基本知识就不在这里阐述了。
算法实现题 8-1 飞行员配对方案问题
问题描述:
第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出
的每一架飞机都需要配备在航行技能和语言上能互相配合的 2 名飞行员, 其中 1 名是英国飞
行员,另 1 名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英
国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的
外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空
军一次能派出最多的飞机。
编程任务:
对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,
使皇家空军一次能派出最多的飞机。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行
员总数(n<100);m 是外籍飞行员数。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。
接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。文件最后以 2
个-1 结束。
结果输出:
程序运行结束时,将最佳飞行员配对方案输出到文件 output.txt 中。第 1 行是最佳飞行
员配对方案一次能派出的最多的飞机数 M。接下来 M 行是最佳飞行员配对方案。每行有 2
个正整数 i 和 j,表示在最佳飞行员配对方案中,飞行员 i 和飞行员 j 配对。
如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’ 。
我们先画个图~
我们发现这题就是求二分图的最大匹配?
好吧,你有两个选择
1.敲KM
2.外籍飞行员放在x集合里面,英国王牌飞行员放在y集合里面,设立源点S,汇点T,S向每个x集合里面的点连一条容量为1的边(限制每个点只能连出一条边),y集合里面每个点连一条容量为1的点到T,每个配对连容量为1的边,跑最大流。
网络流第一个建模。。还体验了一把输出方案的可怕之处。(从此立下没有SPJ || 唯一解 不打方案的flag!)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<queue> 6 7 #define maxn 102 8 9 using namespace std; 10 11 int dis[maxn],dp[maxn][maxn],m; 12 13 bool DFS() 14 { 15 memset(dis,-1,sizeof(dis)); 16 dis[0]=1; 17 queue<int>q; 18 q.push(0); 19 while(!q.empty()) 20 { 21 int u=q.front(); 22 if(u==m+1)break; 23 q.pop(); 24 for(int i=0;i<=m+1;i++)if(dis[i]==-1&&dp[u][i]>0){ 25 dis[i]=dis[u]+1; 26 q.push(i); 27 } 28 } 29 if(dis[m]==-1)return 0; 30 else return 1; 31 } 32 33 int find(int poi,int low) 34 { 35 int a; 36 if(poi==m+1)return low; 37 for(int i=0;i<=m+1;i++)if(dp[poi][i]>0&&dis[i]==dis[poi]+1){ 38 a=find(i,min(low,dp[poi][i])); 39 if(a){ 40 dp[poi][i]-=a; 41 dp[i][poi]+=a; 42 return a; 43 } 44 } 45 return 0; 46 } 47 48 int main() 49 { 50 int a,n,x,y,ans=0; 51 scanf("%d%d%d%d",&n,&m,&x,&y); 52 while(x!=-1&&y!=-1) 53 { 54 dp[x][y]=1; 55 scanf("%d%d",&x,&y); 56 } 57 for(int i=1;i<=n;i++) 58 dp[0][i]=1; 59 for(int i=n+1;i<=m;i++) 60 dp[i][m+1]=1; 61 while(DFS()) 62 { 63 a=find(0,99999999); 64 while(a) 65 { 66 ans+=a; 67 a=find(0,99999999); 68 } 69 } 70 printf("%d",ans); 71 return 0; 72 }
算法实现题 8-2 太空飞行计划问题
问题描述:
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业
性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这
些实验需要使用的全部仪器的集合 I={I1, I2,…In}。 实验 Ej需要用到的仪器是 I 的子集 RjÍI。
配置仪器 Ik的费用为 ck美元。实验 Ej的赞助商已同意为该实验结果支付 pj美元。W 教授的
任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才
能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部
费用的差额。
编程任务:
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n。m 是实验数,n 是仪
器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费
用;接着是该实验需要用到的若干仪器的编号。最后一行的 n 个数是配置每个仪器的费用。
结果输出:
程序运行结束时,将最佳实验方案输出到文件 output.txt 中。第 1 行是实验编号;第 2
行是仪器编号;最后一行是净收益。
一道最小割的好题!求最小割==求最大流。。或者用某专门求最小割的算法
关于一些证明:http://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html
我不信邪再输了一次方案。下面是代码。结果呵呵了。flag继续立起来!
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 8 #define maxn 121 9 10 using namespace std; 11 12 vector <int> graph[maxn]; 13 vector <int> cgraph[maxn]; 14 vector <int> opp[maxn]; 15 16 int n,m,dis[maxn]; 17 18 bool g[maxn]; 19 20 bool DFS() 21 { 22 memset(dis,-1,sizeof(dis)); 23 queue<int>q; 24 dis[0]=1; 25 for(int i=0;i<graph[0].size();i++)if(cgraph[0][i]>0){ 26 int v=graph[0][i]; 27 dis[v]=2; 28 q.push(v); 29 } 30 while(!q.empty()) 31 { 32 int u=q.front(); 33 q.pop(); 34 for(int i=0;i<graph[u].size();i++) 35 if(cgraph[u][i]>0&&dis[graph[u][i]]==-1) 36 { 37 dis[graph[u][i]]=dis[u]+1; 38 q.push(graph[u][i]); 39 if(graph[u][i]==n+1+m)return 1; 40 } 41 } 42 return 0; 43 } 44 45 int find(int poi,int low) 46 { 47 if(poi==n+m+1)return low; 48 for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){ 49 int a=find(graph[poi][i],min(low,cgraph[poi][i])); 50 if(a) 51 { 52 cgraph[poi][i]-=a; 53 int v=opp[poi][i]; 54 cgraph[graph[poi][i]][v]+=a; 55 return a; 56 } 57 } 58 return 0; 59 } 60 61 void addedge(int a,int b,int c) 62 { 63 graph[a].push_back(b); 64 cgraph[a].push_back(c); 65 opp[b].push_back(graph[a].size()-1); 66 graph[b].push_back(a); 67 cgraph[b].push_back(0); 68 opp[a].push_back(graph[b].size()-1); 69 } 70 71 void luangao(int poi) 72 { 73 for(int i=0;i<graph[poi].size();i++) 74 { 75 int flag=0; 76 if(cgraph[poi][i]>0)continue; 77 for(int j=0;j<graph[graph[poi][i]].size();j++) 78 for(int k=0;k<graph[graph[graph[poi][i]][j]].size();k++) 79 if(graph[graph[graph[poi][i]][j]][k]==m+n+1&&cgraph[graph[graph[poi][i]][j]][k]!=0)flag=1; 80 if(flag==0){g[graph[poi][i]]=1;for(int j=0;j<graph[graph[poi][i]].size();j++)g[graph[graph[poi][i]][j]]=1;} 81 } 82 } 83 84 void dfs(int poi) 85 { 86 for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&!g[graph[poi][i]]){ 87 g[graph[poi][i]]=1; 88 dfs(graph[poi][i]); 89 } 90 } 91 92 int main() 93 { 94 freopen("shut.in","r",stdin); 95 freopen("shut.out","w",stdout); 96 int x=1,ans=0,sum=0; 97 char s; 98 scanf("%d%d",&n,&m); 99 for(int i=1;i<=n;i++) 100 { 101 scanf("%d ",&x);ans+=x; 102 addedge(0,i,x); 103 int ss=0; 104 while(1) 105 { 106 scanf("%c",&s); 107 if(s==‘\n‘){ 108 if(ss)addedge(i,ss+n,99999999); 109 break; 110 } 111 else if(s==‘ ‘)addedge(i,n+ss,99999999),ss=0; 112 else ss=ss*10+s-‘0‘; 113 } 114 } 115 for(int i=1;i<=m;i++) 116 scanf("%d",&x),addedge(i+n,n+m+1,x); 117 int a; 118 while(DFS()) 119 { 120 while(a=find(0,99999999)) 121 sum+=a; 122 } 123 dfs(0); 124 // luangao(0); 125 for(int i=1;i<=n;i++) 126 if(g[i]==1)printf("%d ",i); 127 printf("\n"); 128 for(int i=1;i<=m;i++) 129 if(g[i+n]==1)printf("%d ",i); 130 printf("\n%d",ans-sum); 131 return 0; 132 }
算法实现题 8-3 最小路径覆盖问题
问题描述:
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个
顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶
点开始,长度也是任意的,特别地,可以为 0。G 的最小路径覆盖是 G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图 G 的最小路径覆盖。
编程任务:
对于给定的给定有向无环图 G,编程找出 G 的一个最小路径覆盖.
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 n 和 m。n 是给定有向无环图
G 的顶点数, m 是 G 的边数。接下来的 m 行,每行有 2 个正整数 i 和 j,表示一条有向边(i,j)
结果输出:
程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出
一条路径。文件的最后一行是最少路径数。
首先我们知道选择的边数越多,路径就越少。
所以我们只需把每个点拆成两个,放入x,y集合里面,然后按8-1的方法连边,跑最大流,用点数-最大流即可。
这题又要输方案- -但是我拒绝!
View Code
算法实现题 8-4 最小路径覆盖问题
问题描述:
假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,。。的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多可
放 11 个球。
编程任务:
对于给定的 n,计算在 n 根柱子上最多能放多少个球。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示柱子数。
结果输出:
程序运行结束时,将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件
output.txt 中。文件的第一行是球数。接下来的 n 行,每行是一根柱子上的球的编号。
首先直接出答案是不行的啦。。(听说有通项公式 - -)
于是我们枚举答案,然后把能放在相邻的连一条边做最小路径覆盖然后看ans,ans大于答案就输出,否则继续,另外每次不需要额外建图,直接残量网络上加边继续跑就是的啦。。
拒绝方案!
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 8 #define maxn 3202 9 10 using namespace std; 11 12 vector <int> graph[maxn]; 13 vector <int> fgraph[maxn]; 14 vector <int> cgraph[maxn]; 15 16 int pf[61],dis[3202]; 17 18 void addedge(int a,int b,int c) 19 { 20 graph[a].push_back(b); 21 cgraph[a].push_back(c); 22 fgraph[b].push_back(graph[a].size()-1); 23 graph[b].push_back(a); 24 cgraph[b].push_back(0); 25 fgraph[a].push_back(graph[b].size()-1); 26 } 27 28 void prework(int k) 29 { 30 addedge(0,k,1); 31 addedge(k+1600,3201,1); 32 for(int i=sqrt(k)+1;pf[i]-k<k;i++) 33 addedge(pf[i]-k,k+1600,99999999); 34 } 35 36 bool DFS() 37 { 38 memset(dis,-1,sizeof(dis)); 39 dis[0]=1; 40 queue<int>q; 41 for(int i=0;i<graph[0].size();i++)if(cgraph[0][i]>0&&dis[graph[0][i]]==-1){ 42 dis[graph[0][i]]=2; 43 q.push(graph[0][i]); 44 } 45 while(!q.empty()) 46 { 47 int u=q.front(); 48 q.pop(); 49 for(int i=0;i<graph[u].size();i++) 50 { 51 // printf("%d %d",graph[u][i],cgraph[u][i]); 52 if(cgraph[u][i]>0&&dis[graph[u][i]]==-1){ 53 dis[graph[u][i]]=dis[u]+1; 54 q.push(graph[u][i]); 55 if(graph[u][i]==3201)return 1; 56 } 57 } 58 } 59 return 0; 60 } 61 62 int find(int poi,int low) 63 { 64 if(poi==maxn-1)return low; 65 for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){ 66 int a=find(graph[poi][i],min(low,cgraph[poi][i])); 67 if(a){ 68 cgraph[poi][i]-=a; 69 cgraph[graph[poi][i]][fgraph[poi][i]]+=a; 70 return a; 71 } 72 } 73 return 0; 74 } 75 76 int main() 77 { 78 freopen("ball.in","r",stdin); 79 freopen("ball.out","w",stdout); 80 int n; 81 for(int i=1;i<=60;i++)pf[i]=i*i; 82 scanf("%d",&n); 83 int sum=0,a; 84 for(int i=1;;i++) 85 { 86 prework(i); 87 while(DFS()){while(a=find(0,99999999))sum+=a;} 88 if(i-sum>n){printf("%d",i-1);return 0;} 89 } 90 }
算法实现题 8-5 圆桌问题
问题描述:
假设有来自 n 个不同单位的代表参加一次国际会议。每个单位的代表数分别为
ri,i , = 1,2,.., 。会议餐厅共有 m 张餐桌,每张餐桌可容纳ci (i = 1,2,.., m) 个代表就餐。
为了使代表们充分交流, 希望从同一个单位来的代表不在同一个餐桌就餐。 试设计一个算法,
给出满足要求的代表就餐方案。
编程任务:
对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n,m 表示单位数,n 表
示餐桌数,1<=m<=150, 1<=n<=270。文件第 2 行有 m 个正整数,分别表示每个单位的代表
数。文件第 3 行有 n 个正整数,分别表示每个餐桌的容量。
结果输出:
程序运行结束时,将代表就餐方案输出到文件 output.txt 中。如果问题有解,在文件第
1 行输出 1,否则输出 0。接下来的 m 行给出每个单位代表的就餐桌号。如果有多个满足要
求的方案,只要输出 1 个方案。
一眼看出桌子在左边,单位在右边,源点连桌子容量,汇点连代表数,每个桌子连容量为1的边,跑最大流。
View Code
看起来方案很好搞,结果。。呵呵。。
· 算法实现题 8-6 圆桌问题
问题描述:
给定正整数序列x1,.. xn 。
(1)计算其最长递增子序列的长度 s。
(2)计算从给定的序列中最多可取出多少个长度为 s 的递增子序列。
(3)如果允许在取出的序列中多次使用 x1和 xn,则从给定序列中最多可取出多少个长
度为 s 的递增子序列。
编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示给定序列的长度。接
下来的 1 行有 n 个正整数.
结果输出:
程序运行结束时,将任务(1)(2)(3)的解答输出到文件 output.txt 中。第 1 行是最长
递增子序列的长度 s。第 2 行是可取出的长度为 s 的递增子序列个数。第 3 行是允许在取出
的序列中多次使用 x1和 xn时可取出的长度为 s 的递增子序列个数。
第一问先DP艹过。。
然后我们就可以开始建模啦!
1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。
2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。
3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。
4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。
第二问跑最大流,第三问把边(<1.a>,<1.b>)(<N.a>,<N.b>)(S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<cmath> 5 #include<algorithm> 6 7 #define maxn 501 8 9 using namespace std; 10 11 int o[maxn],num[maxn],b=0,dis[maxn*2],n; 12 13 vector<int>graph[maxn*2]; 14 vector<int>cgraph[maxn*2]; 15 vector<int>fgraph[maxn*2]; 16 17 void addedge(int a,int b,int c) 18 { 19 graph[a].push_back(b); 20 cgraph[a].push_back(c); 21 fgraph[b].push_back(graph[a].size()-1); 22 graph[b].push_back(b); 23 cgraph[b].push_back(0); 24 fgraph[a].push_back(graph[b].size()-1); 25 } 26 27 void clear(int kk){ 28 vector <int> neww; 29 vector <int> new1; 30 vector <int> new2; 31 swap(neww,graph[kk]); 32 swap(new1,cgraph[kk]); 33 swap(new2,fgraph[kk]); 34 } 35 36 bool DFS() 37 { 38 memset(dis,-1,sizeof(dis)); 39 dis[0]=1;queue<int>q; 40 q.push(0); 41 while(!q.empty()) 42 { 43 int u=q.front(); 44 q.pop(); 45 for(int i=0;i<graph[u].size();i++)if(cgraph[u][i]>0&&dis[graph[u][i]]==-1){ 46 dis[graph[u][i]]=dis[u]+1; 47 q.push(graph[u][i]); 48 if(graph[u][i]==n*2+1)return true; 49 } 50 } 51 return false; 52 } 53 54 int find(int poi,int low) 55 { 56 if(poi==2*n+1)return low; 57 for(int i=0;i<graph[poi].size();i++)if(cgraph[poi][i]>0&&dis[graph[poi][i]]==dis[poi]+1){ 58 int a=find(graph[poi][i],min(cgraph[poi][i],low)); 59 if(a) 60 { 61 cgraph[poi][i]-=a; 62 cgraph[graph[poi][i]][fgraph[poi][i]]+=a; 63 return a; 64 } 65 } 66 return 0; 67 } 68 69 int main() 70 { 71 freopen("alis.in","r",stdin); 72 freopen("alis.out","w",stdout); 73 scanf("%d",&n); 74 for(int i=1;i<=n;i++) 75 scanf("%d",&o[i]); 76 for(int i=1;i<=n;i++) 77 { 78 int kk=0; 79 for(int j=i-1;j>=1;j--)if(o[j]<=o[i]){ 80 if(!kk)kk=j; 81 else if(num[kk]<num[j])kk=j; 82 } 83 if(!kk)num[i]=1; 84 else num[i]=num[kk]+1; 85 } 86 int maxx=0; 87 for(int i=1;i<=n;i++) 88 maxx=max(maxx,num[i]); 89 printf("%d\n",maxx); 90 for(int i=1;i<=n;i++) 91 { 92 addedge(i,i+n,1); 93 if(num[i]==1)addedge(i+n,n*2+1,1); 94 if(num[i]==maxx)addedge(0,i,1); 95 } 96 for(int i=1;i<=n;i++) 97 for(int j=i+1;j<=n;j++) 98 if(o[j]>=o[i]&&num[j]==num[i]+1)addedge(j+n,i,1); 99 int a,sum=0; 100 while(DFS())while(a=find(0,99999999))sum+=a; 101 printf("%d\n",sum); 102 for(int i=0;i<=2*n+1;i++)clear(i); 103 for(int i=1;i<=n;i++) 104 { 105 if(i==n){ 106 addedge(i,i+n,99999999); 107 if(num[n]==maxx)addedge(0,i,99999999); 108 } 109 if(i==1){ 110 addedge(i,i+n,99999999); 111 addedge(i+n,n*2+1,99999999); 112 } 113 addedge(i,i+n,1); 114 if(num[i]==1)addedge(i+n,n*2+1,1); 115 if(num[i]==maxx)addedge(0,i,1); 116 } 117 for(int i=1;i<=n;i++) 118 for(int j=i+1;j<=n;j++) 119 if(o[j]>=o[i]&&num[j]==num[i]+1)addedge(j+n,i,1); 120 sum=0; 121 while(DFS())while(a=find(0,99999999))sum+=a; 122 printf("%d",sum); 123 return 0; 124 }
标签:
原文地址:http://www.cnblogs.com/tuigou/p/4687177.html