zoj3229 Shoot the Bullet 有源汇上下界最大流

Shoot the Bullet

第一次做上下界网络流,感觉还是挺简单的,主要是在无源汇可行流的基础上 + 二分

题意:Aya要给m个cute girl 拍照片呢,第x个girl at least 要拍Gx张照片,整个拍摄过程要分n天进行,第k天可以给Ck个girl拍,代号分别为Tk1, Tk2.....,TkCk,这一天给她们拍的照片数有限制,范围是[Lki,Rki],同时这一天也有拍照总数限制Dk。求满足所有要求的情况下,能拍的最多照片数。

分析:如果把照片看做流的话,那么这些限制就可以看成流的上下界,最后求的就是一个最大流问题。

建图:加源点s,汇点t,设每个cute girl 组成集合X = {x| cute girl },设每天组成集合Y = {y| day },则可以见3类边<u, v, b, c> b为下界 c为容量

1。<s, x, gx, INF>

2。<x, y, L, R>

3。<y, t, 0, D>

这样问题就可以表示为求s->t的最大流

 

对于上下界网络流,如果求最大流,可以增边<t, s, ans, INF>,如果求最小流,可以增边<t, s, 0, ans>,其中ans为二分答案值,这样就转化成无源汇可行流问题了

 

 

代码
   
     
1 #include < cstdlib >
2 #include < string .h >
3 #include < cstdio >
4   #define INF 0x3fffffff
5   #define NN 1380
6   #define MM 80000
7   #define RAND 0 // 这里为随机值,对结果不影响
8   using namespace std;
9
10 typedef struct node{
11 int v, w, b;
12 struct node * nxt, * op;
13 }NODE;
14
15 NODE edg[MM]; // 保存所有的边
16 NODE * Link[NN]; // 记录节点所在链表的首节点
17 int h[NN]; // 距离标号,记录每个点到汇点的距离,这里的距离指的是层数
18 int num[NN]; // gap优化,标号为i的顶点个数
19 int F[MM]; // 存储每天边的容量,便于更改
20 int c[ 370 ];
21 int d[ 370 ];
22 int deg[NN]; // 每个节点的入边下界与出边下界之差
23 int ans[ 370 ][ 105 ];
24
25 int M, N, idx, S, T, n; // S 表示源点,T表示汇点,n表示节点个数
26 int s, t, MIN, MAX;
27
28 void Add( int u, int v, int c1, int c2, int b){
29 idx ++ ; // idx记得初始化,不然很容易栈溢出
30 edg[idx].v = v;
31 edg[idx].w = c1;
32 edg[idx].b = b;
33 edg[idx].nxt = Link[u];
34 edg[idx].op = edg + idx + 1 ;
35 F[idx] = c1;
36 Link[u] = edg + idx;
37 idx ++ ;
38 edg[idx].v = u;
39 edg[idx].w = c2; // 有向边为0,无向边为c1
40 edg[idx].b = b;
41 edg[idx].nxt = Link[v];
42 edg[idx].op = edg + idx - 1 ;
43 F[idx] = c2;
44 Link[v] = edg + idx;
45 }
46
47 int Min( int a, int b){
48 return a < b ? a : b;
49 }
50
51 int aug( int u, int flow){
52 if (u == T) return flow;
53 int l = flow; // l表示剩余容量
54 int tmp = n - 1 ;
55 for (NODE * p = Link[u]; p; p = p -> nxt){
56 if (h[u] == h[p -> v] + 1 && p -> w){
57 int f = aug(p -> v, Min(l, p -> w));
58 l -= f;
59 p -> w -= f;
60 p -> op -> w += f;
61 if (l == 0 || h[S] == n) return flow - l; // gap
62 }
63 if (p -> w > 0 && h[p -> v] < tmp){
64 tmp = h[p -> v];
65 }
66 }
67 if (l == flow){ // 如果没有找到增流,才修改标号,刚开始写错了,也杯具的过了好多题
68 num[h[u]] -- ; // gap
69 if (num[h[u]] == 0 ) h[S] = n; // gap,每个点的距离值最多为n - 1,这里设为n 表示断层了
70 else {
71 h[u] = tmp + 1 ;
72 num[h[u]] ++ ; // gap
73 }
74 }
75 return flow - l;
76 }
77
78 void Init(){
79 idx = 0 ;
80 S = t + 1 ;
81 T = t + 2 ;
82 n = T + 1 ;
83 memset(Link, 0 , sizeof (Link));
84 }
85 /* n表示总点的个数,包括源点和汇点 */
86 int sap(){
87 int ans = 0 ;
88 memset(h, 0 , sizeof (h)); // h 保存的是距离标号(到汇点的)
89 memset(num, 0 , sizeof (num));
90 num[ 0 ] = n;
91 while (h[S] < n){
92 ans += aug(S, INF);
93 }
94 return ans;
95 }
96
97
98 void Build(){
99 // Init();
100 int i;
101 for (i = 1 ; i <= M + N; i ++ ){
102 if (deg[i] > 0 ) Add(S, i, deg[i], 0 , RAND);
103 else Add(i, T, - deg[i], 0 , RAND);
104 }
105 Add(S, s, 0 , 0 , RAND);
106 Add(t, T, 0 , 0 , RAND);
107 }
108
109 void update( int mid){
110 F[idx - 3 ] = mid - MIN;
111 F[idx - 2 ] = 0 ;
112 F[idx - 1 ] = mid;
113 F[idx] = 0 ;
114
115 int i;
116 for (i = 1 ; i <= idx; i ++ ){
117 edg[i].w = F[i];
118 }
119 }
120
121 int OK(){
122 sap();
123 for (NODE * p = Link[S]; p; p = p -> nxt){
124 if (p -> w) return 0 ;
125 }
126 for (NODE * p = Link[T]; p; p = p -> nxt){
127 if ((p -> op) -> w) return 0 ;
128 }
129
130 int tmp = 2 * M;
131 int i, j;
132 for (i = 1 ; i <= N; i ++ ){
133 for (j = 1 ; j <= c[i]; j ++ ){
134 tmp += 2 ;
135 ans[i][j] = edg[tmp].b + edg[tmp].w;
136 }
137 tmp += 2 ;
138 }
139 return 1 ;
140 }
141 void Solve(){
142 int low = MIN;
143 int hig = MAX;
144 int Flow = - 1 ;
145 while (low <= hig){
146 int mid = (low + hig) / 2 ;
147 update(mid);
148 if (OK()){
149 Flow = mid;
150 low = mid + 1 ;
151 } else hig = mid - 1 ;
152 }
153 if (Flow == - 1 ) puts( " -1 " );
154 else {
155 printf( " %d\n " , Flow);
156 int i, j;
157 for (i = 1 ; i <= N; i ++ ){
158 for (j = 1 ; j <= c[i]; j ++ ){
159 printf( " %d\n " , ans[i][j]);
160 }
161 }
162 }
163 puts( "" );
164 }
165 int main( int argc, char ** argv) {
166 int i, j, g, v, l, r;
167 while (scanf( " %d%d " , & N, & M) != EOF){
168 MIN = 0 ; MAX = 0 ;
169 memset(deg, 0 , sizeof (deg));
170 s = 0 ;
171 t = M + N + 1 ;
172 Init();
173 for (i = 1 ; i <= M; i ++ ){
174 scanf( " %d " , & g);
175 MIN += g;
176 deg[i] += g;
177 deg[s] -= g;
178 Add(s, i, INF, 0 , RAND);
179 }
180 for (i = 1 ; i <= N; i ++ ){
181 scanf( " %d%d " , & c[i], & d[i]);
182 MAX += d[i];
183 for (j = 1 ; j <= c[i]; j ++ ){
184 scanf( " %d%d%d " , & v, & l, & r);
185 deg[v + 1 ] -= l;
186 deg[M + i] += l;
187 Add(v + 1 , M + i, r - l, 0 , l);
188 }
189 Add(M + i, t, d[i], 0 , RAND);
190 }
191 Add(t, s, INF, 0 , RAND); // 增加一条t->s的边
192 Build();
193 Solve();
194 }
195 return 0 ;
196 }
197

 

 

你可能感兴趣的:(最大流)