码迷,mamicode.com
首页 > 其他好文 > 详细

例题11-8 矩阵解压 UVa11082

时间:2015-03-09 09:27:55      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:dinic算法   网络流问题   建模   

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;
}

例题11-8 矩阵解压 UVa11082

标签:dinic算法   网络流问题   建模   

原文地址:http://blog.csdn.net/u014800748/article/details/44140229

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!