例题11-8 矩阵解压 UVa11082

来源:互联网 发布:像初页一样的软件 编辑:程序博客网 时间:2024/04/28 13:30

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];//记录vint 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);//同理,注意为了区分行和列的下标,要多加一个Rfor (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;}

0 0
原创粉丝点击