转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1299340266
提示:最大流问题
折磨了我3天的题。。。网上的前辈都推荐拆点做,但是我没有用拆点(感觉拆点很麻烦)
这道题我用了三种方法去做,但是结果却差强人意。。。。
【BFS+标号法+不拆点】 成功AC
【BFS+压入重标法+不拆点】(WA,不知道错哪里了,找不到反例)
【BFS+压入重标法+模拟拆点】(WA,不知道错哪里了,找不到反例)
AC的程序我贴下面,后两个WA的代码我贴在AC代码下面,希望有达人帮我查出哪里出错了。。。无限感激
题意:
老实说,我完全看不懂题目在说什么= =。。。Orz
不过还是简单概括下:
有N台机器,每台机器有P部分,每部分都有各自的输入、输出规格,因此一台机器有P个输入规格,P个输出规格。每台机器有2*P+1种参数去描述:第一个参数Q:该机器的容量;接下来P个参数S:该机器各部分的输入规格;接下来P个参数D:该机器各部分的输出规格。
其中输入规格有三种情况:0,1,2
0:该部分不能存在
1:该部分必须保留
2:该部分可有可无
输出规格有2种情况:0,1
0:该部分不存在
1:该部分存在
至于这条题要我做什么,我完全不理解= =
不过通过对样例的输入输出的剖析,再加之前人总结出来的一些不清不楚的见解,我能够详尽地分析这题的模型O(∩_∩)O哈哈~
注意:本题可以只有一次输入,一次输出,还有Sample I/O这段英文不用输入输出
Sample input:
P N (N台机器,每台机器有P部分)
接着输入N行,其实每行都是一个结点的信息
每一行的格式为 一个Q P个S P个D
其中Q为当前结点的容量,S都是当前结点的输入规格,D都是输出规格
Sample output:
第一行的两个数字分别表示:最大流的值,流量发生变化的边数M(和s还有t关联的边不在其内,那些不属于原有的边,是附加边)
接下来有M行,每一行都有三个数字,A B W
A B为流量发生变化的边的端点,W为流量的变化值(每条边初始流量为0,最终流量就是找到最大流时的流量)
若图不连通,则输出0 0
解题思路:
首先构造图:
添加两个超级源s,超级汇t
如果某个节点(i)的输入部分不含1,则添加一条s->i路径,容量为Qi;
如果某个节点(j)输出全为1,则添加一条j->t路径,容量为Qj;
如果节点i的输出与j的输入不存在冲突(输出与输入对应位置的和不能为1),则添加一条i->j的路径,容量为min(Qi, Qj).
PS:输出与输入对应位置的和不能为1,就是说组合为可以为00,11, 21或20,但不能是01
解题方法:
就是最大流问题
1 /*【BFS+标号法+不拆点】 成功AC*/
2
3 //Memory Time
4 //292K 0MS
5
6 #include<iostream>
7 using namespace std;
8
9 const int inf=10001;
10 int s; //超级源
11 int t; //超级汇
12
13 int n; //总结点数(包括超级源、超级汇)
14 int p; //每台机器的部分数
15 int cap[52][52];// 边容量
16
17
18 int min(int a,int b)
19 {
20 return a<b?a:b;
21 }
22
23 /*利用BFS找增广链求网络最大流*/
24
25 int maxflow(void)
26 {
27 int queue[52];
28 int head,tail;
29 int pre[52]; //结点i的前驱
30
31 int minflow;
32 int flow = 0;
33 int x,y;
34
35 while(true)
36 {
37 memset(pre, -1, sizeof(pre));
38
39 for(queue[head=tail=0]=s;head<=tail;head++)
40 {
41 x=queue[head];
42 for(int i=0;(i<n) && (pre[t]==-1);i++)//当汇点还没有被标记时
43 if (cap[x][i]>0 && pre[i]==-1) //当结点u指向i的边存在,且i还没有标记前驱时
44 {
45 pre[i]=x;//记录结点i的前驱为u
46 queue[++tail]=i;
47 }
48 }
49
50 if(pre[t]==-1)
51 break;//BFS后汇点没有被标记,则跳出while,已经不存在增广链
52
53 minflow=inf;//初始化
54 for(x=pre[y=t];y!=s;)//回溯
55 {
56 if(cap[x][y] < minflow)
57 minflow=cap[x][y];//寻找当前增广链中最小容量的边,记录其边权(容量)
58 y=x;
59 x=pre[y];
60 }
61
62 for(x=pre[y=t];y!=s;) //当前增广链 流量调整
63 {
64 cap[x][y] -= minflow; //正向弧容量减少
65 cap[y][x] += minflow; //反向弧容量增加
66 y=x;
67 x=pre[y];
68 }
69
70 flow += minflow; //最大流=每次寻得的增广链的调整量之和
71 }
72 return flow;//返回最大流
73 }
74
75 int main(int i,int j,int k)
76 {
77 int in[52][21];
78 int out[52][3];
79 int backup[52][52];//备份图
80 int N; //除超级源、超级汇以外的总结点数
81 int line; //生产线数(容量发生变化的边数)
82 int flow; //最大流
83
84 while (cin>>p>>N)
85 {
86 /*Initial*/
87
88 memset(cap,0,sizeof(cap)); //所有正向弧和反向弧的容量都初始化为0
89
90 s=0;//超级源
91 t=N+1; //超级汇
92 n=N+2; //总结点数+2
93 line=0; //记录变化的边的数量(生产线数量)
94
95 /*Input*/
96
97 for(i=1;i<=N;i++)
98 for(j=0;j<2*p+1;j++)
99 cin>>in[i][j]; //用一个数列存储第i个结点的信息 in[i][0] 为结点i的容量
100
101 bool flag_s, flag_t;
102 for(i=1;i<=N;i++)
103 {
104 flag_s=flag_t=true;
105 for(k=0;k<p;k++)
106 {
107 if(in[i][1+k]==1)
108 flag_s=false; //检查第i个结点的输入序列信息,当输入列不含1时
109 if(in[i][p+1+k]==0)
110 flag_t=false;//检查第i个结点的输出序列信息,当输出列全为1时
111 }
112 if(flag_s)
113 cap[s][i]=in[i][0]; //当输入列不含1时,S->i,边容量为i的容量
114 if(flag_t)
115 cap[i][t]=in[i][0]; //当输出列全为1时,i->t,边容量为i的容量
116
117 bool flag=true;
118 for(j=1;j<=N;j++)
119 if(i!=j)
120 {
121 flag=true;
122 for(k=0;(k<p) && flag;k++)
123 if(in[i][p+1+k]+in[j][1+k]==1) //当第i个结点的第k个输出位,对应第j个结点的第k个输入位之和全不为0时
124 flag=false;
125
126 if(flag)
127 cap[i][j] = min(in[i][0], in[j][0]); //i->j,边容量为i的容量和j的容量的最小值
128 }
129 }
130
131 /*利用BFS找增广链求网络最大流*/
132
133 memcpy(backup, cap, sizeof(cap)); //把寻找增广链前的图的容量信息复制
134 flow=maxflow(); //返回最大流
135
136 /*Output*/
137
138 for(i=1;i<=N;i++) //注意范围,排除了含超级源和超级汇的边
139 for(j=1;j<=N;j++)
140 if (cap[i][j] < backup[i][j])//比较调整前后的边权,若容量减少了,则输出这条边的信息
141 {
142 out[line][0]=i; //i,j为生产线的两端点
143 out[line][1]=j;
144 out[line][2]=backup[i][j] - cap[i][j];//变化的流量值(该生产线的最大生产量)
145 line++;
146 }
147
148 cout<<flow<<' '<<line<<endl;
149 for(i=0;i<line;i++)
150 cout<<out[i][0]<<' '<<out[i][1]<<' '<<out[i][2]<<endl;
151 }
152 return 0;
153 }
==========华丽的分割线===========
1 /*BFS+压入重标法+不拆点 ->WA*/
2
3 #include<iostream>
4 using namespace std;
5
6 const int inf=10001;
7 int s=0; //超级源
8 int t; //超级汇
9
10 int n; //机器数
11 int p; //每台机器的部分数
12 int cap[52][52]; //弧(i,j)的容量
13 int flow[52][52]; //弧(i,j)的流量
14 bool mark[52][52]={false};
15 int sum=0;
16 bool vist[52]; //标记点i是否已标号
17
18 class info //当前点j的标记信息
19 {
20 public:
21 int pre; //当前点j的前驱i
22 int lv; //l(v)
23 int q; //机器(结点i)的生产量(容量)
24 int in[10]; //输入规格
25 int out[10]; //输出规格
26 int nei[51]; //当前结点直接指向的邻居结点
27 int pn; //邻居结点的指针
28 }node[52];
29
30 int min(int a,int b)
31 {
32 return a<b?a:b;
33 }
34
35 void back(void)
36 {
37 int x=t;
38 while(x!=s)
39 {
40 if(x!=t && node[x].pre!=s)
41 {
42 if(!mark[ node[x].pre ][x])
43 sum++; //记录流量发生变化的弧数(含s、t的弧除外)
44 mark[ node[x].pre ][x]=true; //标记弧(i,j)的流量是否发生了变化(含s、t的弧除外)
45 }
46 flow[ node[x].pre ][x] += node[t].lv; //改进增广路
47 x=node[x].pre;
48
49 }
50 return;
51 }
52
53 bool bfs(void)
54 {
55 memset(vist,false,sizeof(vist));
56 vist[s]=true;
57
58 int queue[52];
59 int head=0;
60 int tail=0;
61 queue[tail++]=s;
62
63 while(head<=tail-1) //注意,这是再也找不到增广路的结束条件
64 {
65 int x=queue[head];
66 int y;
67 for(int i=0;i<node[x].pn;i++)
68 {
69 y=node[x].nei[i];
70 if(!vist[y] && flow[x][y]<cap[x][y]) //搜索的目标要求是 未标记 & 非饱和弧
71 {
72 queue[tail++]=y;
73
74 vist[y]=true;
75 node[y].pre=x;
76 node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );
77 }
78 if(vist[t]) //当超级汇被标记
79 break;
80 }
81 if(!vist[t])
82 head++;
83 else
84 return true; //搜索到一条增广路
85 }
86 return false;
87 }
88
89 int main(int i,int j,int k)
90 {
91 /*Input*/
92
93 cin>>p>>n;
94
95 /*Initial*/
96
97 node[s].pre=-1;
98 node[s].lv=inf;
99 t=n+1;
100 for(i=0;i<t;i++)
101 node[i].pn=0;
102
103 /*Input & Structure Graphs*/
104
105 bool sign;
106 for(i=1;i<=n;i++)
107 {
108 sign=false;
109 cin>>node[i].q;
110
111 for(j=0;j<p;j++)
112 {
113 cin>>node[i].in[j];
114 if(node[i].in[j]==1) //如果某个节点(i)的输入部分不含1
115 sign=true;
116 }
117 if(!sign) //则添加一条s->i路径,容量为Qi
118 {
119 node[s].nei[ node[s].pn++ ]=i;
120 cap[s][i]=node[i].q;
121 flow[s][i]=0;
122 }
123
124 sign=false;
125 for(j=0;j<p;j++)
126 {
127 cin>>node[i].out[j];
128 if(node[i].out[j]==0) //如果某个节点(j)输出全为1
129 sign=true;
130 }
131 if(!sign) //则添加一条j->t路径,容量为Qj
132 {
133 node[i].nei[ node[i].pn++ ]=t;
134 cap[i][t]=node[i].q;
135 flow[i][t]=0;
136 }
137 }
138
139 for(i=1;i<=n;i++)
140 for(j=1;j<=n;j++)
141 {
142 sign=false;
143 if(i!=j)
144 {
145 for(k=0;k<p;k++)
146 if((node[i].out[k] + node[j].in[k])==1) //如果节点i的输出与j的输入不存在冲突
147 { //即输出与输入对应位置的和不为1
148 sign=true;
149 break;
150 }
151
152 if(!sign) //则添加一条i->j的路径,容量为min(Qi, Qj).
153 {
154 node[i].nei[ node[i].pn++ ]=j;
155 cap[i][j]=min(node[i].q,node[j].q);
156 flow[i][j]=0;
157 }
158 }
159 }
160
161 /*压入重标法找增广轨*/
162
163 while(true)
164 {
165 if(bfs()) //如果能搜到到增广路
166 back(); //从超级汇开始回溯,改进增广路
167 else
168 {
169 int max=0;
170 for(i=0;i<node[s].pn;i++)
171 max+=flow[s][ node[s].nei[i] ];
172 cout<<max<<' '<<sum<<endl;
173 for(i=1;i<=n;i++)
174 for(j=1;j<=n;j++)
175 if(i!=j && mark[i][j])
176 cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;
177 break;
178 }
179 }
180 return 0;
181 }
==========华丽的分割线===========
1 /*BFS+压入重标法+模拟拆点 ->WA*/
2
3 #include<iostream>
4 using namespace std;
5
6 const int inf=10001;
7 int s=0; //超级源
8 int t; //超级汇
9
10 int n; //机器数
11 int p; //每台机器的部分数
12 int cap[52][52]; //弧(i,j)的容量
13 int flow[52][52]; //弧(i,j)的流量
14 bool mark[52][52]={false};
15 int sum=0;
16 bool vist[52]; //标记点i是否已标号
17
18 class info //当前点j的标记信息
19 {
20 public:
21 int pre; //当前点j的前驱i
22 int lv; //l(v)
23 int q; //机器(结点j)的总生产量(容量)
24 int f; //机器(结点j)的当前生产量(流量)
25 int in[10]; //输入规格
26 int out[10]; //输出规格
27 int nei[51]; //当前结点直接指向的邻居结点
28 int pn; //邻居结点的指针
29 }node[52];
30
31 int min(int a,int b)
32 {
33 return a<b?a:b;
34 }
35
36 void back(void)
37 {
38 int x=t;
39 while(x!=s)
40 {
41 if(x!=t && node[x].pre!=s)
42 {
43 if(!mark[ node[x].pre ][x])
44 sum++; //记录流量发生变化的弧数(含s、t的弧除外)
45 mark[ node[x].pre ][x]=true; //标记弧(i,j)的流量是否发生了变化(含s、t的弧除外)
46 }
47 flow[ node[x].pre ][x] += node[t].lv; //改进增广路
48 node[x].f += node[t].lv; //改进增广路上的顶点
49 x=node[x].pre;
50
51 }
52 return;
53 }
54
55 bool bfs(void)
56 {
57 memset(vist,false,sizeof(vist));
58 vist[s]=true;
59
60 int queue[52];
61 int head=0;
62 int tail=0;
63 queue[tail++]=s;
64
65 while(head<=tail-1) //注意,这是再也找不到增广路的结束条件
66 {
67 int x=queue[head];
68 int y;
69 for(int i=0;i<node[x].pn;i++)
70 {
71 y=node[x].nei[i];
72 if(!vist[y] && flow[x][y]<cap[x][y] && node[y].f<node[y].q) //搜索的目标要求是 未标记 & 非饱和弧 & 非饱和点(模拟拆点)
73 { //当某一顶点满流后,该顶点不能再生产更多的机器
74 queue[tail++]=y;
75
76 vist[y]=true;
77 node[y].pre=x;
78 node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );
79 }
80 if(vist[t]) //当超级汇被标记
81 break;
82 }
83 if(!vist[t])
84 head++;
85 else
86 return true; //搜索到一条增广路
87 }
88 return false;
89 }
90
91 int main(int i,int j,int k)
92 {
93 freopen("in.txt","r",stdin);
94
95 /*Input*/
96
97 cin>>p>>n;
98
99 /*Initial*/
100
101 t=n+1;
102 node[s].pre=-1;
103 node[s].lv=inf;
104 node[t].q=inf;
105 for(i=0;i<=t;i++)
106 {
107 node[i].pn=0;
108 node[i].f=0;
109 }
110
111 /*Input & Structure Graphs*/
112
113 bool sign;
114 for(i=1;i<=n;i++)
115 {
116 sign=false;
117 cin>>node[i].q;
118
119 for(j=0;j<p;j++)
120 {
121 cin>>node[i].in[j];
122 if(node[i].in[j]==1) //如果某个节点(i)的输入部分不含1
123 sign=true;
124 }
125 if(!sign) //则添加一条s->i路径,容量为Qi
126 {
127 node[s].nei[ node[s].pn++ ]=i;
128 cap[s][i]=node[i].q;
129 flow[s][i]=0;
130 }
131
132 sign=false;
133 for(j=0;j<p;j++)
134 {
135 cin>>node[i].out[j];
136 if(node[i].out[j]==0) //如果某个节点(j)输出全为1
137 sign=true;
138 }
139 if(!sign) //则添加一条j->t路径,容量为Qj
140 {
141 node[i].nei[ node[i].pn++ ]=t;
142 cap[i][t]=node[i].q;
143 flow[i][t]=0;
144 }
145 }
146
147 for(i=1;i<=n;i++)
148 for(j=1;j<=n;j++)
149 {
150 sign=false;
151 if(i!=j)
152 {
153 for(k=0;k<p;k++)
154 if((node[i].out[k] + node[j].in[k])==1) //如果节点i的输出与j的输入不存在冲突
155 { //即输出与输入对应位置的和不为1
156 sign=true;
157 break;
158 }
159
160 if(!sign) //则添加一条i->j的路径,容量为min(Qi, Qj).
161 {
162 node[i].nei[ node[i].pn++ ]=j;
163 cap[i][j]=min(node[i].q,node[j].q);
164 flow[i][j]=0;
165 }
166 }
167 }
168
169 /*压入重标法找增广轨*/
170
171 while(true)
172 {
173 if(bfs()) //如果能搜到到增广路
174 back(); //从超级汇开始回溯,改进增广路
175 else
176 {
177 int max=0;
178 for(i=0;i<node[s].pn;i++)
179 max+=flow[s][ node[s].nei[i] ];
180 cout<<max<<' '<<sum<<endl;
181 for(i=1;i<=n;i++)
182 for(j=1;j<=n;j++)
183 if(i!=j && mark[i][j])
184 cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;
185 break;
186 }
187 }
188 return 0;
189 }