HDU 3338:Kakuro Extension(脑洞大开的网络流)

http://acm.hdu.edu.cn/showproblem.php?pid=3338

题意:在一个n*m的地图里面,有黑方块和白方块,黑方块可能是“XXXXXXX”或者“YYY/YYY”,这里的YYY代表可能为数字,如果是在“/”左边出现数字,代表在它下面的该列的白方块的和加起来要等于这个数字,如果是在“/”右边出现数字,代表它右边的该行的白方块的和加起来要等于这个数字。我们要做的就是求出这些白方块上的数字,并按照要求输出。

思路:看完题意一脸懵逼,想了一个下午还是不知道怎么写。无奈只能看下别人的做法。

因为所有的有行数字的黑方块加起来的和等于所有的有列数字的黑方块加起来的和,所以可以把列看作是源点,把行看作是汇点,然后把有关系的白块和他们连接起来,添加一个超级源点S和列的黑方块相连,添加一个超级汇点T和行的黑方块相连,这样就可以建出图了。至于边的容量,因为是带上下限的网络流(下限是1,上限是9),为了变成没有下限的网络流,所以看作容量上下限为(0-8),这样,所以我选择对白块拆点,两点之间的容量是8,然后列的黑方块和第一个点相连,第二个点和行的黑方块相连,容量都设为INF。超级源点S与列的黑方块的容量和行的黑方块与超级汇点T的容量分别为其方块上的值减去它们对应的白方块数(因为白方块的容量-1了)。最后得到的白方块的拆点的边的容量,答案再+1就转化回来了。还有记得数组要开大点。。一开始忘了开大点T了1次。

网络流真是神奇啊!

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <queue>
  5 using namespace std;
  6 #define N 200005
  7 #define M 1000005
  8 #define INF 0x3f3f3f3f
  9 struct Edge {
 10     int u, v, nxt, cap;
 11     Edge () {}
 12     Edge (int u, int v, int nxt, int cap) : u(u), v(v), nxt(nxt), cap(cap) {}
 13 } edge[M];
 14 struct node {
 15     int id, cid, rid, cval, rval, type;
 16 } mp[105][105];
 17 int head[N], tot, cur[N], pre[N], gap[N], dis[N], S, T, cnt[N];
 18 
 19 void Add(int u, int v, int cap) {
 20     edge[tot] = Edge(u, v, head[u], cap); head[u] = tot++;
 21     edge[tot] = Edge(v, u, head[v], 0); head[v] = tot++;
 22 }
 23 
 24 void BFS() {
 25     queue<int> que;
 26     memset(dis, INF, sizeof(dis));
 27     memset(gap, 0, sizeof(gap));
 28     dis[T] = 0; que.push(T);
 29     while(!que.empty()) {
 30         int u = que.front(); que.pop();
 31         for(int i = head[u]; ~i; i = edge[i].nxt) {
 32             int v = edge[i].v;
 33             if(dis[v] == INF) {
 34                 dis[v] = dis[u] + 1;
 35                 gap[dis[v]]++;
 36                 que.push(v);
 37             }
 38         }
 39     }
 40 }
 41 
 42 int ISAP(int n) {
 43     BFS();
 44     memcpy(cur, head, sizeof(cur));
 45     int u = pre[S] = S, ans = 0, i, flow, index;
 46     while(dis[S] < n) {
 47         if(u == T) {
 48             flow = INF;
 49             for(i = S; i != T; i = edge[cur[i]].v)
 50                 if(flow > edge[cur[i]].cap) flow = edge[cur[i]].cap, index = i;
 51             for(i = S; i != T; i = edge[cur[i]].v)
 52                 edge[cur[i]].cap -= flow, edge[cur[i]^1].cap += flow;
 53             u = index; ans += flow;
 54         }
 55         for(i = cur[u]; ~i; i = edge[i].nxt) if(dis[edge[i].v] == dis[u] - 1 && edge[i].cap > 0) break;
 56         if(~i) {
 57             pre[edge[i].v] = u; cur[u] = i; u = edge[i].v;
 58         } else {
 59             int md = n + 1;
 60             if(--gap[dis[u]] == 0) break;
 61             for(i = head[u]; ~i; i = edge[i].nxt)
 62                 if(dis[edge[i].v] < md && edge[i].cap > 0) md = dis[edge[i].v], cur[u] = i;
 63             gap[dis[u] = md + 1]++;
 64             u = pre[u];
 65         }
 66     }
 67     return ans;
 68 }
 69 
 70 int main() {
 71     int n, m;
 72     while(~scanf("%d%d", &n, &m)) {
 73         char s[10]; tot = 0; int rowcnt = 0, colcnt = 0, white = 0;
 74         memset(head, -1, sizeof(head));
 75         memset(cnt, 0, sizeof(cnt));
 76         memset(mp, 0, sizeof(mp));
 77         for(int i = 1; i <= n; i++) {
 78             for(int j = 1; j <= m; j++) {
 79                 scanf("%s", s); int flag = 0, num = 0;
 80                 if(strcmp(s, "XXXXXXX") == 0) continue;
 81                 if(strcmp(s, ".......") == 0) { mp[i][j].type = 1; white++; mp[i][j].id = white; continue; }
 82                 for(int k = 0; k < 3; k++)
 83                     if(s[k] == 'X') { flag = 1; break; }
 84                     else num = num * 10 + s[k] - '0';
 85                 if(!flag) { mp[i][j].cval = num; colcnt++; mp[i][j].cid = colcnt;}
 86                 flag = 0, num = 0;
 87                 for(int k = 4; k < 7; k++)
 88                     if(s[k] == 'X') { flag = 1; break; }
 89                     else num = num * 10 + s[k] - '0';
 90                 if(!flag) { mp[i][j].rval = num; rowcnt++; mp[i][j].rid = rowcnt;}
 91                 mp[i][j].type = 2;
 92             }
 93         }
 94         S = 0; T = white * 2 + rowcnt + colcnt + 1;
 95         for(int i = 1; i <= n; i++) {
 96             for(int j = 1; j <= m; j++) {
 97                 if(mp[i][j].type == 2) {
 98                     if(mp[i][j].cval > 0) { // 如果黑块上列有数字
 99                         int now = mp[i][j].cid;
100                         for(int k = i + 1; k <= n; k++) { // 搜这一列上白块
101                             if(mp[k][j].type == 1) {
102                                 Add(now + white * 2, mp[k][j].id, INF);
103                                 mp[i][j].cval--; // 对应的列有白块,这个列容量-1
104                             } else break; // 不是白块就退出
105                         }
106                         Add(S, now + white * 2, mp[i][j].cval);
107                     }
108                     if(mp[i][j].rval > 0) { // 如果黑块上行有数字
109                         int now = mp[i][j].rid;
110                         for(int k = j + 1; k <= m; k++) { // 搜这一行的白块
111                             if(mp[i][k].type == 1) {
112                                 Add(mp[i][k].id + white, now + colcnt + white * 2, INF);
113                                 mp[i][j].rval--; // 对应的行容量-1
114                             } else break;
115                         }
116                         Add(now + colcnt + white * 2, T, mp[i][j].rval);
117                     }
118                 } else if(mp[i][j].type == 1) Add(mp[i][j].id, mp[i][j].id + white, 8); // 白块拆点
119             }
120         }
121         int ans = ISAP(T + 1);
122         for(int u = 1; u <= white; u++) {
123             for(int i = head[u]; ~i; i = edge[i].nxt) { // 暴力搜拆点的边
124                 if(edge[i].v == u + white) {
125                     cnt[u] = 8 - edge[i].cap; // 这条边的流量为 初始cap - 当前cap
126                 }
127             }
128         }
129         for(int i = 1; i <= n; i++) {
130             for(int j = 1; j <= m; j++) {
131                 if(mp[i][j].type == 1) printf("%d", cnt[mp[i][j].id] + 1);
132                 else putchar('_');
133                 if(j != m) putchar(' ');
134                 else putchar('
');
135             }
136         }
137     }
138     return 0;
139 }
原文地址:https://www.cnblogs.com/fightfordream/p/6337353.html