uva11082 网络流经典模型 建图详解

本文介绍了一种利用网络流算法解决特定矩阵重构问题的方法。该问题要求根据给定的行和列和构造一个矩阵,且矩阵元素限定在1到20之间。文章详细阐述了如何构建网络流模型,包括节点定义、边权重设定等关键步骤,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:知道矩阵的前i行之和,和前j列之和(任意i和j都可以)。求这个矩阵。每个格子中的元素必须在1~20之间。矩阵大小上限20*20


首先我们可以随意写出一个答案满足行的限制,然后就需要在每行和不变的情况下,通过调度,满足列的限制,而这个调度的过程,同网络流的流的思想正式契合的,这是网络流思想的很好体现。

建图就是源点连向每个行表示这行的和,然后每列连向汇点,表示这列的和,然后i行向j列连边,表示格子(i,j)将一部分和从行i流向了列j

还有一个小问题就是每个格子的范围是1-20,这样就是一个容量有上下界的问题了,但是实际上可以每个格子都同时加一,就可以完美的避免了将问题复杂化

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define maxn 509

using namespace std;

int n, m, s, t, head[maxn], to[2 * maxn], Next[2 * maxn], flow[2 * maxn], vis[maxn], d[maxn];
int ans[21][21], cur[maxn], tot;

void add (int u, int v, int w)
{
	//if (w) printf ("add %d %d == %d\n", u, v, w);
	to[++tot] = v;
	Next[tot] = head[u];
	head[u] = tot;
	flow[tot] = w;
}

bool bfs ()
{
	memset (vis, 0, sizeof (vis));
	queue <int> q;
	q.push (s);
	vis[s] = 1;
	d[s] = 0;
	while (!q.empty ())
	{
		int now = q.front ();
		q.pop ();
		for (int i = head[now]; i; i = Next[i])
			if (!vis[to[i]] && flow[i] > 0)
			{
				vis[to[i]] = 1;
				q.push (to[i]);
				d[to[i]] = d[now] + 1;
			}
	}
	return vis[t];
}

int dfs (int x, int a)
{
	int ret = 0, f;
	if (x == t || !a)
		return a;
	for (int &i = cur[x]; i; i = Next[i])
		if (d[to[i]] == d[x] + 1 && (f = dfs (to[i], min (a, flow[i]))) > 0)
		{
			flow[i] -= f;
			flow[i ^ 1] += f;
			ret += f;
			a -= f;
			if (!a)
				break;
		}
	return ret;
}

void work ()
{
	while (bfs ())
	{
		rep (i, s, t)
			cur[i] = head[i];
		dfs (s, 0x7fffffff);
	}
}

void print (int x)
{
	printf ("Matrix %d\n", x);
	rep (i, 2, n + 1)
		for (int j = head[i]; j; j = Next[j])
			if (to[j] != s)
				ans[i - 1][to[j] - 1 - n] = 20 - flow[j];// printf ("flow ----- %d  -> %d == %d\n", i, to[j], 19 - flow[j]);
	for (int i = 1; i <= n; i++, cout << endl)
		for (int j = 1; j <= m; j ++, printf (" "))
			printf ("%d", ans[i][j]);
}

int main ()
{
	int ti;
	cin >> ti;
	rep (ff, 1, ti)
	{
		memset (head, 0, sizeof (head));
		memset (Next, 0, sizeof (Next));
		tot = 1;
		cin >> n >> m;
		s = 1, t = 1 + n + m + 1;
		int u, pre = 0;
		rep (i, 1, n)
			scanf ("%d", &u),u -= pre, pre += u, add (s, i + 1, u - m), add (i + 1, s, 0);
		pre = 0;
		rep (i, 1, m)
			scanf ("%d", &u),u -= pre, pre += u, add (1 + n + i, t, u - n), add (t, 1 + n + i, 0);
		rep (i, 1, n)
			rep (j, 1, m)
				add (i + 1, j + 1 + n, 19), add (j + 1 + n, i + 1, 0);
		work ();
		print (ff);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值