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