网络流基本知识就不在这里阐述了。
实现题 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 }
View Code
算法实现题 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
行是仪器编号;最后一行是净收益。
一道最小割的好题!求最小割==求最大流。。或者用某专门求最小割的算法
关于一些证明:
我不信邪再输了一次方案。下面是代码。结果呵呵了。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 }
View Code
算法实现题 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 }
View Code
算法实现题 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 }
View Code