HDU3605 网络流 状压

HDU3605 网络流 状压

题解其他地方已经说的很清楚了 就是不能一个人一个人作为单个点 而是把去去不同星球的状态压缩 作为点 因为10个星球最多只有2^10种去法 比如10011 就是去第一个第三个第四个不去第一第二个 那么有多少个人处于这个状态 就从源点向这个状态连容量为这么多的边 然后每个状态对应的 如果能去i星球就连容量为Inf的边 最后所有的星球向汇点连容量为星球承载量的边 跑一边dinic 如果最大流等于所有人的话就表示可以 不等于就表示不行 rua

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<algorithm>
#include<map>
#define MS(x) memset(x,0,sizeof(x));
#define MS1(x) memset(x,-1,sizeof(x));
using namespace std;
const int maxN = 2000;//点数
const int maxM = 1e6;//边数必须得是给定边数的两倍 因为还有反向边

const int inf = 0x3f3f3f3f;


class Graph
{
	//先Graph G 生成一个类
	//然后G.init(n) n是点的个数
	//然后addedge加边
	//记得设置源点汇点 set_st(start,end);
	//最后直接跑Dinic() 返回的就是最大流
private:
	int s, t;//源点和汇点
	int n;//点的个数
	int cnt;//前向星
	int Head[maxN];//每个点最后一条边的编号
	int Next[maxM];//指向对应点的前一条边
	int W[maxM];//每一条边的残量
	int V[maxM];//每一条边指向的点
	int Depth[maxN];//分层图的深度
	int cur[maxN];//cur就是记录当前点u循环到了哪一条边
public:
	void init(int n)//源点 汇点 点的个数
	{
		cnt = -1;
		memset(Head, -1, sizeof(Head));
		memset(Next, -1, sizeof(Next));
		this->n = n;
	}
	void set_st(int source, int terminal)//设置源点 汇点
	{
		s = source;
		t = terminal;
	}
	void _Add(int u, int v, int w)//前向星
	{
		cnt++;
		Next[cnt] = Head[u];
		Head[u] = cnt;
		V[cnt] = v;
		W[cnt] = w;
	}
	void Add_Edge(int u, int v, int w)//前向星
	{
		_Add(u, v, w);
		_Add(v, u, 0);
	}
	int dfs(int u, int flow)//u是当前节点,flow是当前流量
	{
		if (u == t)//达到汇点 直接返回
			return flow;
		for (int& i = cur[u]; i != -1; i = Next[i])//注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到记录当前弧的目的
		{
			if ((Depth[V[i]] == Depth[u] + 1) && (W[i] != 0))//如果满足分层图条件并且参量不为0
			{
				int di = dfs(V[i], min(flow, W[i]));//向下增广
				if (di > 0)//增广成功
				{
					W[i] -= di;//正向边减
					W[i ^ 1] += di;//反向边加
					return di;//返回流量
				}
			}
		}
		return 0;//增广失败 返回0
	}
	bool bfs()
	{
		queue<int> Q;
		while (!Q.empty()) Q.pop();
		MS(Depth);
		Depth[s] = 1;//源点的深度为1
		Q.push(s);
		while (!Q.empty())
		{
			int u = Q.front();
			Q.pop();
			for (int i = Head[u]; i != -1; i = Next[i])
				if ((Depth[V[i]] == 0) && (W[i] > 0))//深度等于0并且残量大于0
				{
					Depth[V[i]] = Depth[u] + 1;//它就是下一层
					Q.push(V[i]);
				}
		}
		if (!Depth[t]) return 0;//如果汇点的深度为0 也就是不存在增广路
		return 1;//汇点深度不为0 存在增广路
	}
	int Dinic()
	{
		int Ans = 0;//纪录最大流量
		while (bfs())//存在增广路
		{
			for (int i = 1; i <= n; i++)//当前弧优化
				cur[i] = Head[i];
			while (int d = dfs(s, inf))//源点汇入inf的流量 当还能增广时 一直增加流量
			{
				Ans += d;
			}
		}
		return Ans;//返回流量
	}
}G;

int num[1 << 10];

int n, m;
int planet(int n) { return (1 << m) + n ; }
int main()
{
	ios::sync_with_stdio(false);
	while (cin >> n >> m)
	{
		MS(num);
		bool canlive;
		for (int i = 1; i <= n; i++)
		{
			int status = 0;
			for (int j = 1; j <= m; j++)
			{
				cin >> canlive;
				if (canlive) status += 1 << (j - 1);
			}
			num[status]++;
		}
		int s = (1 << m) + m + 1;
		int t = s + 1;
		G.init(t);
		G.set_st(s, t);
		for (int i = 0; i < (1 << m); i++) G.Add_Edge(s, i + 1, num[i]);
		for (int i = 0; i < (1 << m); i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (i&(1 << j)) G.Add_Edge(i + 1, planet(j + 1), inf);
			}
		}
		int capacity;
		for (int i = 1; i <= m; i++)
		{
			cin >> capacity;
			G.Add_Edge(planet(i), t, capacity);
		}
		if (G.Dinic() == n) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
}
发布了9 篇原创文章 · 获赞 1 · 访问量 585

猜你喜欢

转载自blog.csdn.net/weixin_43149897/article/details/83042804