题意:问能否构造一个矩阵,使得矩阵中的每个数字范围是1-20并且满足矩阵每行之和与矩阵每列之和与所给数组相等。
思路:尽管矩阵是20*20 ,但是如果爆搜复杂度依旧爆炸,我们考虑使用网络流解决这个问题,下面重点讲解怎么构图。至于怎么写可以参考紫书,与网上的模板。
建图:先来考虑本题的限制是什么,首先i行与j列 存在着一个关系 即共用元素k[i][j],如果我们把数组A[i]和B[j]分别看成节点,那么意味着A[i]可以流向B[j]大小最多为k[i][j]的流量至少为1的流量,注意到,网络流中流量最小为0,所以我们可以先给每个元素减1 同时算出减完后的A’[i]与B’[j] 求出这个问题下的k’[i][j] 之后k[i][j]=k[i][j]’+1 即可,我们现在可以给A’[i]连一条通向B’[j]的容量为19的边,同时建立一个原点s ,它与每个A’[i]节点连一条边 容量为A’[i]的值 ,建立一个汇点t ,每个B’[j]连一条通向t的边,容量为 B’[j] 跑一遍最大流,如果汇点t的流量之和为sum(B[j])(j from 1 to m) 那么意味可以构造出来 并且k’[i][j] = A’[i]到B’[j]的流量
//主代码来自刘汝佳
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 50 + 5;
const int INF = 1000000000;
struct Edge {
int from, to, cap, flow;
Edge(int u, int v, int c, int f) :from(u), to(v), cap(c), flow(f) {}
};
struct EdmondsKarp {
int n, m;
vector<Edge> edges; // 边数的两倍
vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
int a[maxn]; // 当起点到i的可改进量
int p[maxn]; // 最短路树上p的入弧编号
void init(int n) {
for (int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap) {
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
int Maxflow(int s, int t) {
int flow = 0;
for (;;) {
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = INF;
while (!Q.empty()) {
int x = Q.front(); Q.pop();
for (int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if (!a[e.to] && e.cap > e.flow) {
p[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
Q.push(e.to);
}
}
if (a[t]) break;
}
if (!a[t]) break;
for (int u = t; u != s; u = edges[p[u]].from) {
edges[p[u]].flow += a[t];
edges[p[u] ^ 1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
};
EdmondsKarp g;
int no[maxn][maxn];
int a[maxn], b[maxn], str[maxn][maxn];
int main() {
int R, C, v, kase = 0;
scanf("%d%d", &R, &C);
g.init(R + C + 2);
for (int i = 1; i <= R; i++) {
scanf("%d", &v);
g.AddEdge(0, i, v - C); // row sum is v - last
a[i] = v;
}
int sum_b = 0;
for (int i = 1; i <= C; i++) {
scanf("%d", &v);
g.AddEdge(R + i, R + C + 1, v - R); // col sum is v - last
b[i] = v;
sum_b += v;
}
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++) {
g.AddEdge(i, R + j, 19);
no[i][j] = g.edges.size() - 2; // no[i][j] is the index of arc for cell(i,j)
}
g.Maxflow(0, R + C + 1);
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++)
str[i][j] = g.edges[no[i][j]].flow + 1;
for (int i = 1; i <= R; i++) {
int sum = 0;
for (int j = 1; j <= C; j++) {
sum += str[i][j];
if (str[i][j] < 1 || str[i][j]>20) { printf("No\n"); return 0; }
}
if (sum != a[i]) { printf("No\n"); return 0; }
}
for (int j = 1; j <= C; j++) {
int sum = 0;
for (int i = 1; i <= R; i++)
sum += str[i][j];
if (sum != b[j]) { printf("No\n"); return 0; }
}
printf("Yes\n");
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++)
printf("%d ", g.edges[no[i][j]].flow + 1); // we subtracted 1 from every cell
printf("\n");
}
return 0;
}