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('\n'); 135 } 136 } 137 } 138 return 0; 139 }