1.题目描述:点击打开链接
2.解题思路:本题的突破口在于建模,其实关于最大流的问题大多数难点都在建模上。本题只告诉了我们前i行,前i列的和值,让求解整个矩阵。事先可以算出第i行的和值和第i列的和值。然后该怎么办呢?由于每个元素都是1~20之间的,因此如果把所有元素都减去1,那么正好是0·19之间,因此联想到每条边的容量是19。此时行的和值要减去C,列的和值减去R。根据网络流的性质:流入结点的流量等于流出结点的流量。因此可以从此着手构造模型:假设每行对应一个X结点,每列对应一个Y结点,然后增加源点s,汇点t。对于每个结点Xi,从s到Xi连接一条弧,容量是A[i]-C(此时所有的A[i],B[i]均值第i行,第i列的和值);从Yi到t连接一条弧,容量是B[i]-R。对于每个结点(Xi,Yj),从Xi到Yj连接一条弧,容量是19。这样的一个网络便满足了之前题目中的所有性质。接下来只用求出s-t的最大流,那么如果s出发和到达t都满载,说明问题有解。结点Xi->Yj的流量就是格子(i,j)减1的值。本题的一个新知识点是int的最大上界表示:~0U>>1,另一个知识点是采用了链表结构的Dinic算法的写法。
3.代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include<set> #include<vector> #include<stack> #include<map> #include<queue> #include<deque> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<functional> using namespace std; const int INF = ~0U >> 1;//即int的最大值 struct Dinic { static const int N = 510, M = 100010; int head[N];//链表头部,存储以u开始的边序号 int en[M];//记录v int Next[M];//记录下一条边的序号 int cap[M];//记录容量 int tot;//边数 void clear() { memset(head, 0, sizeof(head)); tot = 1; } void add(int u, int v, int w) { en[++tot] = v; Next[tot] = head[u]; head[u] = tot; cap[tot] = w; } void Add(int u, int v, int w){ add(u, v, w); add(v, u, 0); } int d[N], cur[N]; int n, s, t; bool bfs() { queue<int>q; memset(d, -1, sizeof(d)); d[s] = 0; q.push(s); while (!q.empty()) { int x = q.front(); q.pop(); for (int k = head[x],v; k; k = Next[k]) if (cap[k] && d[v = en[k]]==-1) { d[v] = d[x] + 1; q.push(v); } } return d[t] != -1; } int dfs(int x, int y) { if (x == t || !y)return y; int z = y; for (int&k = cur[x],v; k; k = Next[k]) if (cap[k] && d[v = en[k]] == d[x] + 1) { int w = dfs(v, min(cap[k], z)); cap[k] -= w;//流量增大意味着净容量减少 cap[k ^ 1] += w;//反向边容量表示了正向边的流量 z -= w; if (!z)break; } if (z == y)d[x] = -1; return y - z; } int Maxflow(int s, int t) { this->s = s, this->t = t; int flow = 0; while (bfs()) { for (int i = 1; i <= n; i++) cur[i] = head[i]; flow += dfs(s, INF); } return flow; } }g; int r[25], c[25], R, C, rnd; void solve() { scanf("%d%d", &R, &C); for (int i = 1; i <= R; i++)scanf("%d", r + i); for (int i = 1; i <= C; i++)scanf("%d", c + i); int s = R + C + 1, t = R + C + 2; g.n = R + C + 2; for (int i = 1; i <= R; i++)g.Add(s, i, r[i] - r[i - 1] - C);//r[i]-r[i-1]就是第i行的和值 for (int i = 1; i <= C; i++)g.Add(R + i, t, c[i] - c[i - 1] - R);//同理,注意为了区分行和列的下标,要多加一个R for (int i = 1; i <= R;i++) for (int j = 1; j <= C; j++) g.Add(i, R + j, 19); g.Maxflow(s, t); printf("Matrix %d\n", ++rnd); for (int i = 1; i <= R; i++) { vector<int>ans; for (int k = g.head[i]; k; k = g.Next[k]) if (g.en[k] != g.s)ans.push_back(g.cap[k ^ 1] + 1);//k的反向边的容量表示k的流量 for (int j = ans.size() - 1; j >= 0; j--)//由于新的边位于链表首位,因此要逆序输出 printf("%d%c", ans[j], j ? ' ' : '\n'); } } int main() { //freopen("t.txt", "r", stdin); int T; cin >> T; while (T--) { g.clear(); solve(); if (T)puts(""); } return 0; }
时间: 2024-10-23 05:53:15