http://acm.jlu.edu.cn/joj/showproblem.php?pid=2453
传送门
首先看到这题,想到了去年多校得时候一个网络流,由于上次得出的经验,先用二进制状态压缩点是必须的了。关键是怎么处理满意度是2 和满意度是1 的问题了
去年的那个题是1 和 0的所以直接连边就可以了。此题我们看到每个candy提供的满意度至少为1,所以第一个要做的就是把1分离出去,转为上面的模型。然后把每个人的需求量变为 b[i] / 2 + (b[i] & 1),然后流出最大流是flow 看flow + n >= sum(b)判断就可以了,最关键的是分离一个1的想法,经典的构图题……(也可能是我太水了)
代码如下,另外是我最新的网络流模板,唯一的优先就是比原来的清晰好看了一点,而且拍起来更舒服……
1 /*
2 * =====================================================================================
3 *
4 * Filename: network.cpp
5 *
6 * Description: network problems
7 *
8 * Version: 1.0
9 * Created: 2011/5/9 17:56:48
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-acm-group
15 *
16 * =====================================================================================
17 */
18 #include < iostream >
19 #include < cstring >
20 #include < cstdio >
21 #include < numeric >
22 #include < algorithm >
23 #include < cstdlib >
24 #include < cassert >
25 #define OP(i) (((i) - (pool))^1)
26 using namespace std;
27
28 class sap
29 {
30 private :
31 const static int V = 2010 , E = 1000000 , INF = 100000000 ;
32 int dis[V], numdis[V];
33 bool reachS[V], reachT[V];
34 struct edge
35 {
36 int v, cap;
37 edge * nxt;
38 }pool[E], * g[V], * pp;
39 edge * e[V], * pree[V]; // e保存当前弧,pree存可行流中的边
40 int pre[V], maxflow;
41 void bfs( int v, int n) // 从汇点开始按照反向边流量走
42 {
43 int que[V], tail = 0 ;
44 bool vst[V] = { 0 };
45 memset(numdis, 0 , sizeof (numdis));
46 fill(dis, dis + n, n);
47 dis[v] = 0 ; vst[v] = 1 ; que[ 0 ] = v;
48 for ( int j = 0 ;j <= tail;j ++ )
49 {
50 int tmp = que[j % n];
51 for (edge * i = g[tmp];i != NULL;i = i -> nxt)
52 {
53 if (pool[OP(i)].cap > 0 && ! vst[i -> v])
54 {
55 tail ++ ; vst[i -> v] = 1 ;
56 que[tail % n] = i -> v;
57 dis[i -> v] = dis[tmp] + 1 ;
58 numdis[dis[i -> v]] ++ ;
59 }
60 }
61 }
62 }
63 int findArgumentPath( int & v, int s, int t)
64 {
65 while (e[v] != NULL)
66 {
67 if (e[v] -> cap > 0 && dis[v] == dis[e[v] -> v] + 1 )
68 {
69 pre[e[v] -> v] = v;
70 pree[e[v] -> v] = e[v];
71 v = e[v] -> v;
72 if (v == t)
73 {
74 int minf = INF;
75 for ( int i = t;i != s;i = pre[i])
76 minf = min(minf,pree[i] -> cap);
77 for ( int i = t;i != s;i = pre[i])
78 {
79 pree[i] -> cap -= minf;
80 pool[OP(pree[i])].cap += minf;
81 }
82 v = s;
83 return minf;
84 }
85 }
86 else e[v] = e[v] -> nxt;
87 }
88 return 0 ;
89 }
90 public :
91 int maxflowsap( int n, int s, int t)
92 {
93 bfs(t, n);
94 int v = s;
95 copy(g, g + n, e);
96 while (dis[s] < n) // 标号为n 表示无可行流
97 {
98 int add = findArgumentPath(v, s, t);
99 maxflow += add;
100 if (add == 0 ) // 发现某个点v没有允许弧,维护其距离标号
101 {
102 int mindis = INF;
103 numdis[dis[v]] -- ;
104 if ( ! numdis[dis[v]]) break ; // GAP 优化,发现断层直接退出
105 for (edge * i = g[v];i != NULL;i = i -> nxt)
106 if (i -> cap > 0 ) mindis = min(mindis,dis[i -> v] + 1 );
107 if (mindis == INF) dis[v] = n;
108 else dis[v] = mindis;
109 numdis[dis[v]] ++ ;
110 e[v] = g[v]; // 改变距离标号以后从新维护当前弧,从他的前驱重新流
111 if (v != s) v = pre[v];
112 }
113 }
114 return maxflow;
115 }
116 void firststart()
117 {
118 pp = pool;maxflow = 0 ;
119 memset(g, 0 , sizeof (g));
120 // memset(reachS, 0, sizeof(reachS));
121 // memset(reachT, 0, sizeof(reachT));
122 } // 后两个用于求割等问题
123 void addedge( int i, int j, int cap)
124 {
125 pp -> v = j;
126 pp -> cap = cap;
127 pp -> nxt = g[i];
128 g[i] = pp ++ ;
129 } // 不自动加反向边
130 void dfss( int x)
131 {
132 reachS[x] = true ;
133 for (edge * i = g[x];i != NULL ;i = i -> nxt)
134 if (i -> cap && ! reachS[i -> v]) dfss(i -> v);
135 } // 网络流割S-T割出来的S集合是从源点正向边遍历到的点集合
136 void dfst( int x)
137 {
138 reachT[x] = true ;
139 for (edge * i = g[x];i != NULL; i = i -> nxt)
140 if (pool[OP(i)].cap && ! reachT[i -> v]) dfst(i -> v);
141 } // 用于求关键边等问题
142 // 关键边是一端reachS 一端reachT 且无流量的边
143 }one;
144 const int N = 10 ;
145 const int M = 1 << N;
146 const int INF = 1000000000 ;
147 int b[N], cnt[M];
148 int main()
149 {
150 int t, n, m;
151 scanf( " %d " , & t);
152 while (t -- )
153 {
154 scanf( " %d %d " , & n, & m);
155 one.firststart();
156 memset(cnt, 0 , sizeof (cnt));
157 for ( int i = 0 ;i < n;i ++ )
158 {
159 int s = 0 , p;
160 for ( int j = 0 ;j < m;j ++ )
161 {
162 scanf( " %d " , & p);
163 if (p == 2 ) s |= ( 1 << j);
164 }
165 cnt[s] ++ ;
166 }
167 for ( int i = 0 ;i < m;i ++ ) scanf( " %d " , & b[i]);
168 int sum = accumulate(b, b + m, 0 );
169 int s = ( 1 << m) + m, t = s + 1 ;
170 for ( int i = 0 ;i < ( 1 << m);i ++ )
171 {
172 if (cnt[i] == 0 ) continue ;
173 one.addedge(s, i, cnt[i]);
174 one.addedge(i, s, 0 );
175 for ( int j = 0 ;j < m;j ++ )
176 {
177 if ((i & ( 1 << j)) == 0 ) continue ;
178 one.addedge(i, ( 1 << m) + j, INF);
179 one.addedge(( 1 << m) + j, i, 0 );
180 }
181 }
182 for ( int i = 0 ;i < m;i ++ )
183 {
184 one.addedge(( 1 << m) + i, t, b[i] / 2 + (b[i] & 1 ));
185 one.addedge(t, ( 1 << m) + i, 0 );
186 }
187 int flow = one.maxflowsap(t + 1 , s, t);
188 if (flow + n >= sum) printf( " Yes\n " );
189 else printf( " No\n " );
190
191 }
192 return 0 ;
193 }
补一道题:话说了自己做了这么久得网络流的,还是不太理解割的定义和一些求解的问题,割是否唯一,关键割之类的问题也比较含糊
这里说一下POJ 1815 这道经典的网络流问题,要求是求关键字最小的割。
我刚开始就想问,割不是不唯一的吗?那么怎么求,转而骂自己一句废话,要是唯一的还用关键字最小吗?
那么此题就是要每句每一条边,然后重新求最大流,如果比原来的小的话,更新最大流(忘记更新了WA几次,其实还是没有从本质上理解到位)删边,迭代一个过程。
巨挫无比的代码见这里:
1 /*
2 * =====================================================================================
3 *
4 * Filename: network.cpp
5 *
6 * Description: network problems
7 *
8 * Version: 1.0
9 * Created: 2011/5/9 17:56:48
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-acm-group
15 *
16 * =====================================================================================
17 */
18 #include < iostream >
19 #include < cstring >
20 #include < cstdio >
21 #include < numeric >
22 #include < algorithm >
23 #include < cstdlib >
24 #include < cassert >
25 #define OP(i) (((i) - (pool))^1)
26 using namespace std;
27
28 class sap
29 {
30 private :
31 const static int V = 60000 , E = 2000000 , INF = 100000000 ;
32 int dis[V], numdis[V];
33 bool reachS[V], reachT[V];
34 struct edge
35 {
36 int v, cap;
37 edge * nxt;
38 }pool[E], * g[V], * pp;
39 edge * e[V], * pree[V]; // e保存当前弧,pree存可行流中的边
40 int pre[V], maxflow;
41 void bfs( int v, int n) // 从汇点开始按照反向边流量走
42 {
43 int que[V], tail = 0 ;
44 bool vst[V] = { 0 };
45 memset(numdis, 0 , sizeof (numdis));
46 fill(dis, dis + n, n);
47 dis[v] = 0 ; vst[v] = 1 ; que[ 0 ] = v;
48 for ( int j = 0 ;j <= tail;j ++ )
49 {
50 int tmp = que[j % n];
51 for (edge * i = g[tmp];i != NULL;i = i -> nxt)
52 {
53 if (pool[OP(i)].cap > 0 && ! vst[i -> v])
54 {
55 tail ++ ; vst[i -> v] = 1 ;
56 que[tail % n] = i -> v;
57 dis[i -> v] = dis[tmp] + 1 ;
58 numdis[dis[i -> v]] ++ ;
59 }
60 }
61 }
62 }
63 int findArgumentPath( int & v, int s, int t)
64 {
65 while (e[v] != NULL)
66 {
67 if (e[v] -> cap > 0 && dis[v] == dis[e[v] -> v] + 1 )
68 {
69 pre[e[v] -> v] = v;
70 pree[e[v] -> v] = e[v];
71 v = e[v] -> v;
72 if (v == t)
73 {
74 int minf = INF;
75 for ( int i = t;i != s;i = pre[i])
76 minf = min(minf,pree[i] -> cap);
77 for ( int i = t;i != s;i = pre[i])
78 {
79 pree[i] -> cap -= minf;
80 pool[OP(pree[i])].cap += minf;
81 }
82 v = s;
83 return minf;
84 }
85 }
86 else e[v] = e[v] -> nxt;
87 }
88 return 0 ;
89 }
90 public :
91 int maxflowsap( int n, int s, int t)
92 {
93 bfs(t, n);
94 int v = s;
95 copy(g, g + n, e);
96 while (dis[s] < n) // 标号为n 表示无可行流
97 {
98 int add = findArgumentPath(v, s, t);
99 maxflow += add;
100 if (add == 0 ) // 发现某个点v没有允许弧,维护其距离标号
101 {
102 int mindis = INF;
103 numdis[dis[v]] -- ;
104 if ( ! numdis[dis[v]]) break ; // GAP 优化,发现断层直接退出
105 for (edge * i = g[v];i != NULL;i = i -> nxt)
106 if (i -> cap > 0 ) mindis = min(mindis,dis[i -> v] + 1 );
107 if (mindis == INF) dis[v] = n;
108 else dis[v] = mindis;
109 numdis[dis[v]] ++ ;
110 e[v] = g[v]; // 改变距离标号以后从新维护当前弧,从他的前驱重新流
111 if (v != s) v = pre[v];
112 }
113 }
114 return maxflow;
115 }
116 void firststart()
117 {
118 pp = pool;maxflow = 0 ;
119 memset(g, 0 , sizeof (g));
120 // memset(reachS, 0, sizeof(reachS));
121 // memset(reachT, 0, sizeof(reachT));
122 } // 后两个用于求割等问题
123 void addedge( int i, int j, int cap)
124 {
125 // if(i == 0) printf("%d %d %d\n", i, j, cap);
126 pp -> v = j;
127 pp -> cap = cap;
128 pp -> nxt = g[i];
129 g[i] = pp ++ ;
130 } // 不自动加反向边
131 void dfss( int x)
132 {
133 reachS[x] = true ;
134 for (edge * i = g[x];i != NULL ;i = i -> nxt)
135 if (i -> cap && ! reachS[i -> v]) dfss(i -> v);
136 } // 网络流割S-T割出来的S集合是从源点正向边遍历到的点集合
137 void dfst( int x)
138 {
139 reachT[x] = true ;
140 for (edge * i = g[x];i != NULL; i = i -> nxt)
141 if (pool[OP(i)].cap && ! reachT[i -> v]) dfst(i -> v);
142 } // 用于求关键边等问题
143 // 关键边是一端reachS 一端reachT 且无流量的边
144 }one;
145 const int N = 201 ;
146 const int INF = 100000 ;
147 #define BEG(i) ((i) * 2)
148 #define END(i) ((i) * 2 + 1)
149 int maps[N][N];
150 int n, s, t;
151 int build( int x)
152 {
153 int S = BEG(s), T = END(t);
154 one.firststart();
155 for ( int i = 0 ;i < n;i ++ )
156 {
157 if (i == s || i == t)
158 {
159 one.addedge(BEG(i), END(i), INF);
160 one.addedge(END(i), BEG(i), 0 );
161 }
162 else if (i != x)
163 {
164 one.addedge(BEG(i), END(i), 1 );
165 one.addedge(END(i), BEG(i), 0 );
166 }
167 }
168 for ( int i = 0 ;i < n;i ++ )
169 for ( int j = 0 ;j < n;j ++ )
170 {
171 if (maps[i][j] && i != x && j != x)
172 {
173 one.addedge(END(i), BEG(j), INF);
174 one.addedge(BEG(j), END(i), 0 );
175 }
176 }
177 return one.maxflowsap(n * 2 , S, T);
178 }
179 int main()
180 {
181 while (scanf( " %d %d %d " , & n, & s, & t) == 3 )
182 {
183 s -- , t -- ;
184 for ( int i = 0 ;i < n;i ++ )
185 for ( int j = 0 ;j < n;j ++ )
186 scanf( " %d " , & maps[i][j]);
187 int maxflow = build( - 1 );
188 if (maxflow == INF)
189 printf( " NO ANSWER!\n " );
190 else
191 {
192 cout << maxflow << endl;
193 for ( int i = 0 ;i < n;i ++ )
194 {
195 if (i != s && i != t && build(i) < maxflow)
196 {
197 maxflow -- ;
198 printf( " %d " ,i + 1 );
199 for ( int j = 0 ;j < n;j ++ )
200 maps[i][j] = maps[j][i] = 0 ;
201 }
202 }
203 puts( "" );
204 }
205 }
206 return 0 ;
207 }
目前的计划是继续图论的练习和新算法的学习……不要继续这么水了fight for golden!!!