poj_2396 有上下界的网络流

题目大意

    一个mxn的矩阵,给出矩阵中每一行的和sh[1,2...m]以及每一列的数字的和目sv[1,2...n],以及矩阵中的一些元素的范围限制,比如a[1][2] > 1, a[2][3] < 4, a[3][3] = 10等等,且要求矩阵中的每个元素值非负。求出,是否存在满足所有给出的限制条件的矩阵,若存在输出。

题目分析

    这么多限制条件。。开始只能想到暴力解法+剪枝。这他妈得需要多大的脑洞才能无中生有的想到网络流解法? 不过,给出网络流的想法之后发现采用网络流解法确实很合理,很简单(唯一不好的是建图太费劲!!)。 
    考虑将每行视为一个节点,每列视为一个节点,这样就有m个行节点和n个列节点,m个行节点和n个列节点之间的直接通路mxn个,这些通路上的流量视为我们要求的矩阵的元素值。 
    那么,如何利用限制条件来构造图? 
添加一个源点s和一个汇点t。将每一行的数字和视为从s流入该行对应节点的上下界相同的流,每一列的数字和视为从该列对应的节点流到t的上下界相同的流 
然后,将矩阵元素(i, j)的范围限制在图中用行节点i到列节点j之间的一条有流量上下界的边表示。那么,行节点i的流入量(只有s点流入行节点)总和为该行的数字总和,同时该行节点连接到n个列节点,这n条边上的流量分别对应元素[i,1][i,2].... 且若该网络存在可行流我们对图节点流量和矩阵元素求和的对应。 
    根据矩阵元素的限制条件,构造图,该图为一个有源汇有上下界的网络流。采用求解有源汇流量有上下界的网络可行流的解法:先将有源汇转换为无源汇,然后求解可行流 
 
ps: 麻蛋,写了五六个小时,中间各种bug,累感无爱。。 总结出一个规律:在程序求解多个步骤,多种因素对某一个变量的影响的时候,常常可以总结规律,不需要每次有一个因素起作用的时候都修改目标变量,可以先将各个因素/各个步骤的影响记录下来,最后再修改目标变量。 
    这样做,基本都是可以提高效率(降低时间复杂度和编码复杂度,简化逻辑),甚至有时候这就应该是合理的逻辑。

实现(c++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define MAX_NODE 250
#define MAX_EDGE_NUM 50000
#define INFINITE 1 << 25
#define min(a, b) a<b? a:b
#define max(a,b) a>b? a:b
//============================= 以下为 寻找最大网络流的 ISAP算法 部分 =============================
struct Edge{        //定义边
    int from;
    int to;
    int w;
    int next;
    int rev;
    bool operator == (const pair<int, int>& p){
        return from == p.first && to == p.second;
    }
};
Edge gEdges[MAX_EDGE_NUM];
 
int gEdgeCount;
int gFlow[MAX_NODE][MAX_NODE];
int gGap[MAX_NODE];
int gDist[MAX_NODE];
int gHead[MAX_NODE];
int gPre[MAX_NODE];
int gPath[MAX_NODE];
int ss, tt, sum1, sum2, m, n;
int gSource, gDestination;
void InsertEdge(int u, int v, int w){
    Edge* it = find(gEdges, gEdges + gEdgeCount, pair<int, int>(u, v));
    if (it != gEdges + gEdgeCount){
        if (u == ss || v == tt){
            it->w += w;
        }
        else{
            it->w = w;
        }
    }
    else{
        int e1 = gEdgeCount;
        gEdges[e1].from = u;
        gEdges[e1].to = v;
        gEdges[e1].w = w;
        gEdges[e1].next = gHead[u];
        gHead[u] = e1;
 
        gEdgeCount++;
        int e2 = gEdgeCount;
        gEdges[e2].from = v;
        gEdges[e2].to = u;
        gEdges[e2].w = 0;
        gEdges[e2].next = gHead[v];
        gHead[v] = e2;
 
        gEdges[e1].rev = e2;
        gEdges[e2].rev = e1;
        gEdgeCount++;
    }
 
}
 
void Bfs(){
    memset(gGap, 0, sizeof(gGap));
    memset(gDist, -1, sizeof(gDist));
    gGap[0] = 1;
    gDist[gDestination] = 0;
    queue<int>Q;
    Q.push(gDestination);
    while (!Q.empty()){
        int n = Q.front();
        Q.pop();
        for (int e = gHead[n]; e != -1; e = gEdges[e].next){
            int v = gEdges[e].to;
            if (gDist[v] >= 0)
                continue;
            gDist[v] = gDist[n] + 1;
            gGap[gDist[v]] ++;
            Q.push(v);
        }
    }
}
 
int ISAP(int n){ // n为节点的数目
    Bfs();
    int u = gSource;
    int e, d;
    int ans = 0;
    while (gDist[gSource] <= n){
        if (u == gDestination){ //增广
            int min_flow = INFINITE;
            for (e = gPath[u]; u != gSource; e = gPath[u = gPre[u]]){ //注意,先u = gPre[u], 再取 e = gPath[u]
                min_flow = min(min_flow, gEdges[e].w);
            }
            u = gDestination;
            for (e = gPath[u]; u != gSource; e = gPath[u = gPre[u]]){
                gEdges[e].w -= min_flow;
                gEdges[gEdges[e].rev].w += min_flow;
 
                gFlow[gPre[u]][u] += min_flow;
                gFlow[u][gPre[u]] -= min_flow;  //这个不能少,注意!!!!
            }
            ans += min_flow;
        }
        for (e = gHead[u]; e != -1; e = gEdges[e].next){
            if (gEdges[e].w > 0 && gDist[u] == gDist[gEdges[e].to] + 1)
                break;
        }
        if (e >= 0){ //向前推进
            gPre[gEdges[e].to] = u; //前一个点
            gPath[gEdges[e].to] = e;//该点连接的前一个边
            u = gEdges[e].to;
        }
        else{
            d = n;
            for (e = gHead[u]; e != -1; e = gEdges[e].next){
                if (gEdges[e].w > 0) //需要能够走通才行!!
                    d = min(d, gDist[gEdges[e].to]);
            }
            if (--gGap[gDist[u]] == 0) //gap优化
                break;
 
            gDist[u] = d + 1;       //重标号
 
            ++gGap[gDist[u]];   //更新 gap!!
            if (u != gSource)
                u = gPre[u];//回溯
        }
    }
    return ans;
}
 
//=================================以上为 ISAP 算法 ============================
 
//将图打印出来,用于debug
void print_graph(int n){
    for (int u = 0; u < n; u++){
        printf("node %d links to ", u);
        for (int e = gHead[u]; e != -1; e = gEdges[e].next)
            printf("%d(flow = %d) ", gEdges[e].to, gEdges[e].w);
        printf("\n");
    }
}
 
//保存 第i行第j列的数字的范围
struct Node{
    int min_val;
    int max_val;
};
Node gNodes[250][25];
 
//设置数字的范围
bool SetDataCons(Node& node, char op, int val){
    if (op == '='){
        if (node.max_val != -1 && node.max_val < val){
            return false;
        }
        if (node.min_val != -1 && node.min_val > val){
            return false;
        }
        node.max_val = node.min_val = val;
    }
    else if (op == '>'){
        if (node.max_val != -1 && node.max_val <= val){
            return false;
        }
        node.min_val = max(node.min_val, val + 1);
    }
    else if (op == '<'){
        if (val <= 0){
            return false;
        }
        if (node.min_val != -1 && node.min_val >= val){
            return false;
        }
        if (node.max_val != -1)
            node.max_val = min(node.max_val, val - 1);
        else
            node.max_val = val - 1;
    }
    return true;
}
 
//建图
void BuildGraph(){
    //注意,第i行,第j列,对应node结构体的 gNodes[i][j],并且对应,最后的图中的节点为 i和 j+m(m为行数)
    for (int i = 1; i <= m; i++){
        for (int j = 1; j <= n; j++){
            if (gNodes[i][j].max_val == -1){
                if (gNodes[i][j].min_val == -1){
                    InsertEdge(i, j + m, INFINITE);
                }
                else{
                    sum1 += gNodes[i][j].min_val;
                    InsertEdge(i, tt, gNodes[i][j].min_val);
                    InsertEdge(ss, j + m, gNodes[i][j].min_val);
                    InsertEdge(i, j + m, INFINITE);
 
                    gFlow[i][j + m] = gNodes[i][j].min_val; //边的下限,先加入到最后的边的流量中
                }
            }
            else {
                if (gNodes[i][j].min_val == -1){
                    InsertEdge(i, j + m, gNodes[i][j].max_val);
                     
                }
                else{
                    sum1 += gNodes[i][j].min_val;
 
                    InsertEdge(i, tt, gNodes[i][j].min_val);
                    InsertEdge(ss, j + m, gNodes[i][j].min_val);
 
                    if (gNodes[i][j].max_val > gNodes[i][j].min_val)
                        InsertEdge(i, j + m, gNodes[i][j].max_val - gNodes[i][j].min_val);
 
                    gFlow[i][j + m] = gNodes[i][j].min_val;//边的下限,先加入到最后的边的流量中
                }
            }
        }
    }
}
int main(){
 
    int c, w, u, v, cas, val;
    char op;
    scanf("%d", &cas);
    while (cas --){
        scanf("%d %d", &m, &n);
        memset(gNodes, -1, sizeof(gNodes));
        gEdgeCount = 0;
        memset(gEdges, 0, sizeof(gEdges));
 
        memset(gFlow, 0, sizeof(gFlow));
        memset(gHead, -1, sizeof(gHead));
 
        gSource = 0, gDestination = n + m + 1;
        ss = n + m + 2, tt = n + m + 3;
        sum1 = 0, sum2 = 0;
        bool valid_data = true;
        //输入的过程中,先插入一些边 从 SS 出发的
        for (int i = 1; i <= m; i++){
            scanf("%d", &w);
            if (valid_data == false)
                continue;
            if (w < 0){
                valid_data = false;
            }
            sum1 += w;
            InsertEdge(ss, i, w);
        }
         
        InsertEdge(gSource, tt, sum1);
        //输入的过程中,先插入一些边 到达 TT的
        for (int i = m + 1; i <= m + n; i++){
            scanf("%d", &w);
            if (valid_data == false)
                continue;
            if (w < 0)
                valid_data = false;
            sum2 += w;
            InsertEdge(i, tt, w);
        }
 
        InsertEdge(ss, gDestination, sum2);
     
        if (sum1 != sum2){
            valid_data = false;
        }
 
        sum1 = sum2 = (sum1 + sum2);
 
        //设置 矩阵中数字的范围
        scanf("%d", &c);
        for (int i = 0; i < c; i++){
            scanf("%d %d %c %d", &u, &v, &op, &val);
            if (valid_data == false)
                continue;
 
            if (w < 0){
                if (op == '=' || op == '<')
                    valid_data = false;
                continue;
            }
 
            if (u == 0){
                if (v == 0){
                    for (int i = 1; i <= m; i++){
                        for (int j = 1; j <= n; j++){
                            if (valid_data == false){
                                break;
                            }
                            valid_data = SetDataCons(gNodes[i][j],op, val);
                        }
                    }
                }
                else{
                    for (int i = 1; i <= m; i++){
                        if (valid_data == false){
                            break;
                        }
                        valid_data = SetDataCons(gNodes[i][v], op, val);
                    }
                }
            }
            else{
                if (v == 0){
                    for (int j = 1; j <= n; j++){
                        if (valid_data == false){
                            break;
                        }
                        valid_data = SetDataCons(gNodes[u][j], op, val);
                    }
                }
                else{
                    valid_data = SetDataCons(gNodes[u][v], op, val);
                }
            }
        }
        if (valid_data == false){
            printf("IMPOSSIBLE\n\n");
            continue;
        }
        //插入 t-->s的边,容量无穷大,使得有源汇的网络变为无源汇的网络
        InsertEdge(gDestination, gSource, INFINITE);
        //建图
        BuildGraph();
 
        gSource = ss;
        gDestination = tt;
//      print_graph(n + m + 4);
        //找最大流
        int ans = ISAP(n + m + 4);
 
        if (ans != sum1){
            printf("IMPOSSIBLE\n\n");
            continue;
        }
        //输出各个边的流量, 从图中节点i到节点j的流量,即为 矩阵 中 [i][j-m]的数字的大小
        for (int i = 1; i <= m; i++){
            for (int j = m+1; j <= m+n; j++){
                printf("%d ", gFlow[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}

 

posted @   农民伯伯-Coding  阅读(399)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示