网络流初步总结

今天终于把kuangbin大佬的网络流专题给做完了,感觉这些题都是很棒的。在网络流题目中,比熟练掌握各种算法更重要的,是根据不同的题目建立一张图,有些题目看似与图论无关的题目,硬是可以抽象出一张图来,下面随意总结几种建图方法

1.拆点法

如果题目对通过某个点的流量有要求,可以把这个点拆成两个点,其中一个入点和一个出点,从入点到出点有一条容量为流量限制的边,然后以该点为终点的边都连到入点上,以该点为起点的边都连到出点上。

题目:HDU-2732UVa-1658

2.已知矩阵的每一行和每一列的和,求出一个满足条件的矩阵,其中矩阵的元素满足[1,max]

每一行当作一个点,每一列当作一个点。由于网络流允许出现零流,但是数据范围不允许,从超级源点s到每一行加一条容量为该行的和减去列数的边,从每一列的点建一条到汇点的流量为该列列的和减去行数的边,从每一行到每一列建一条容量为max-1的边,流量表示该行该列交叉处元素数的值-1,最后输出时加一就行了。其他数据范围同理

题目:初级版UVa-11082,高级版:HDU-3338

3.状态压缩

HDU-3605

该题中点的数量(100000)远大于状态的数量(1024),可以把相同状态的点合并,节省时间,不然会tle

细节见代码

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxv = 1500, maxe = 30000;
struct Edge
{
	int to, residual, nxt;
	Edge() {}
	Edge(int v,int cap,int nxt):to(v),residual(cap),nxt(nxt){}
} edge[maxe];
int n, m, ecnt, vnum,s,t;
int head[maxv];
void addEdge(int u, int v, int c)
{
	edge[ecnt++] = Edge(v, c, head[u]);
	edge[ecnt++] = Edge(u, 0, head[v]);
	head[u] = ecnt - 2;
	head[v] = ecnt - 1;
}
int cure[maxv], d[maxv];
bool vis[maxv];
bool bfs()
{
	queue<int> q;
	q.push(s);
	memset(vis, 0, sizeof(bool)*vnum);
	d[s] = 0;
	vis[s] = true;
	while (!q.empty())
	{
		int x = q.front(); q.pop();
		for (int cur = head[x]; cur != -1; cur = edge[cur].nxt)
		{
			Edge& e = edge[cur];
			if (e.residual && !vis[e.to])
			{
				vis[e.to] = true;
				d[e.to] = d[x] + 1;
				q.push(e.to);
			}
		}
	}
	return vis[t];
}
int dfs(int x, int a)
{
	if (x == t || a == 0) return a;
	int flow = 0, f;
	for (int& cur = cure[x]; cur != -1; cur = edge[cur].nxt)
	{
		Edge& e = edge[cur];
		if (d[e.to] == d[x] + 1 && (f= dfs(e.to, min(a, e.residual))) > 0)
		{
			a -= f;
			flow += f;
			e.residual -= f;
			edge[cur ^ 1].residual += f;
			if (a == 0) break;
		}
	}
	return flow;
}
int dinic()
{
	int flow = 0;
	while (bfs())
	{
		memcpy(cure, head, sizeof(int)*vnum);
		flow += dfs(s, 0x3f3f3f3f);
	}
	return flow;
}
int num[1024];//保存该状态的点的数量
int main()
{
	while (~scanf("%d%d", &n, &m))
	{
		vnum = (1<<m) +  m + 2;
		s = vnum - 2;
		t = vnum - 1;
		ecnt = 0;
		memset(head, -1, sizeof(int)*vnum);
		memset(num, 0, sizeof(int)*(1 << m));
		for (int i = 0,w; i < n; i++)
		{
			int x = 0;
			for (int j = 0; j < m; j++)
			{
				scanf("%d", &w);
				if (w) x |= (1 << j);
			}
			num[x]++;
		}
		for (int i = 0; i < (1 << m); i++)
			if (num[i])
			{
				addEdge(s, i, num[i]);
				for (int j = 0; j < m; j++)
					if (i&(1 << j))
						addEdge(i, (1 << m) + j, num[i]);
			}
		for (int i = 0,c; i < m; i++)
		{
			scanf("%d", &c);
			addEdge((1<<m)+ i,t, c);
		}
		printf("%s\n", dinic() == n ? "YES" : "NO");
	}
	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值