题意就是
给一个50 * 50的矩阵
然后给出每行每列元素的和
和一个初始矩阵
矩阵中有些是未知,有些是已知
然后我们求目标矩阵就是把能确定的元素的值求出来,实在不能确定的就置为-1
所有矩阵元素的值在0-100之间
看到范围很小。
第一反应是求一个最大流
先把已经给出的元素都从每行每列的和中减掉。
然后左边为行结点,右边为列结点
然后源点向行结点连边
列结点向汇点连边
行和列中如果对应的元素未知就连一下,流向上限是100
然后这样我们就得到了一个可行解
但是可能有多解怎么办
对于一个可能多解的元素
如果我们将这个元素的值固定住。
然后建立一个超级源点与该行结点连边。
该列结点与超级汇点连边。
流量都是1,
跑一遍看看有没有增广路。
如果有,显然这个位置的值是可以改变的,就是多解,然后我们把这个位置的元素值-1,因为我们刚才增广了,其他有元素的值增加了1,所以
为了保持流量的平衡,这个位置的元素要减1
但是这样还不行。
我们想想。
如果该位置的值现在是0怎么办。
他没法减掉1。
所以我们就要想想残余网络了。
既然他没法减掉1,就让他想办法+1,让别的元素-1去
那么我们可以用一个超级源点连接列结点。
行结点连接超级汇点
跑最大流,看有没有增广路。
也就是看他的残余网络能不能减掉1.即它自身+1
如果有增广路,跟之前一样,更新一下边
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cstdlib> #include <cmath> #define MAXN 106 #define MAXM 211111 #define INF 1111111111 using namespace std; struct EDGE { int v, next; int w; }edge[MAXM]; int head[MAXN], e; void init() { memset(head, -1, sizeof(head)); e = 0; } void add(int u, int v, int w) { edge[e].v = v; edge[e].w = w; edge[e].next = head[u]; head[u] = e++; edge[e].v = u; edge[e].w = 0; edge[e].next = head[v]; head[v] = e++; } int n; int h[MAXN]; int gap[MAXN]; int src, des; int tt[111][111]; int dfs(int pos, int cost) { if(pos == des) return cost; int j, minh = n - 1; int lv = cost, d; for(j = head[pos]; j != -1; j = edge[j].next) { int v = edge[j].v; int w = edge[j].w; if(w > 0) { if(h[v] + 1 == h[pos]) { if(lv < edge[j].w) d = lv; else d = edge[j].w; d = dfs(v, d); edge[j].w -= d; edge[j ^ 1].w += d; lv -= d; if(h[src] >= n) return cost - lv; if(lv == 0) break; } if(h[v] < minh) minh = h[v]; } } if(lv == cost) { --gap[h[pos]]; if(gap[h[pos]] == 0) h[src] = n; h[pos] = minh + 1; ++gap[h[pos]]; } return cost - lv; } int sap() { int ret = 0; memset(gap, 0, sizeof(gap)); memset(h, 0, sizeof(h)); gap[0] = n; while(h[src] < n) ret += dfs(src, INF); return ret; } int nt, m; int col[55], row[55]; int val[55][55], id[55][55]; int ans[55][55]; int vis[55][55]; int main() { //freopen("C:/C.in", "r", stdin); //freopen("C:/C2.out", "w", stdout); while(scanf("%d%d", &nt, &m) != EOF) { if(!nt && !m) break; for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) scanf("%d", &val[i][j]); for(int i = 1; i <= nt; i++) scanf("%d", &row[i]); for(int i = 1; i <= m; i++) scanf("%d", &col[i]); memset(ans, -1, sizeof(ans)); for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) { if(val[i][j] != -1) { row[i] -= val[i][j]; col[j] -= val[i][j]; ans[i][j] = val[i][j]; } } init(); src = nt + m + 1; des = nt + m + 2; n = des; int S = nt + m + 3; int T = nt + m + 4; for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) { if(ans[i][j] == -1) { id[i][j] = e; add(i, j + nt, 100); } } for(int i = 1; i <= nt; i++) { add(src, i, row[i]); } for(int j = 1; j <= m; j++) { add(j + nt, des, col[j]); } sap(); n = T; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) if(ans[i][j] != -1) vis[i][j] = 2; src = S; des = T; for(int i = 1; i <= nt; i++) { for(int j = 1; j <= m; j++) { if(vis[i][j] == 2) continue; int te = id[i][j]; int tmp = edge[te ^ 1].w; edge[te].w = edge[te ^ 1].w = 0; int flag = 1; int le = e; add(src, i, 1); int me = e; add(j + nt, des, 1); if( tmp && sap()) flag = 0, tmp--; edge[le].w = edge[le ^ 1].w = 0; edge[me].w = edge[me ^ 1].w = 0; le = e; add(src, j + nt, 1); me = e; add(i, des, 1); if(flag && 100 - tmp > 0&& sap()) flag = 0, tmp++; edge[le].w = edge[le ^ 1].w = 0; edge[me].w = edge[me ^ 1].w = 0; edge[te ^ 1].w = tmp; edge[te].w = 100 - tmp; vis[i][j] = flag; } } for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) { if(vis[i][j] == 2) { if(ans[i][j] != -1) printf("%d", ans[i][j]); } else { if(vis[i][j]) printf("%d", edge[id[i][j] ^ 1].w); else printf("-1"); } if(j < m) printf(" "); else printf("\n"); } } return 0; }