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

 

 

posted on 2010-10-12 20:40  ylfdrib  阅读(1240)  评论(0编辑  收藏  举报