您的位置:首页 > 其它

HDU 3605 Escape(最大流+状态压缩)

2017-08-15 20:38 537 查看
HDU 3605 Escape(最大流+状态压缩)
http://acm.hdu.edu.cn/showproblem.php?pid=3605
题意:

       现有n个人要移居到m个星球去,给定一个n*m的矩阵,第 i 行第 j 列如果为1,表示第 i 个人可以去第 j 个星球,如果为0,表示不可以去。然后给出这m个星球都最多分别能住多少人,问你n个人是不是都能找到星球住? (1 <= n <= 100000), (1 <= m<= 10)

分析:

       明显的最大流问题,不过n的数目最多100W,如果直接这么算(即连数百w的容量为1的边)肯定超时. 这里m最多只有10个,所以一个人对于星球的选择最多有2^10=1024种,所以我们图左边的节点可以用这1024种不同的选择来表示.

       左边节点表示选择方案(编号从0到1023),右边的节点表示m个星球(编号从1023+1到1023+m),源点s为1024+m,汇点t为1025+m.

       比如左边的节点为7时(二进制形式为00 0000 0111),即表示选择第0,1,2这三个星球,所以我们连接7号节点与右边的0号,1号,2号星球节点,容量为 只能选0,1,2三个星球的人的总数目.

       源点s到每个选择方案i有边(s,i,用该方案的人数)

       方案i中如果有选择星球j,那么有边(i,j,INF)

       星球j到汇点t有边(j,t,can[j])
can[j]表示星球j能容纳的最大人数.


最终我们看 max_flow 是否== n 即可.

AC代码:

//最大流模板,可处理重边
//且节点编号从1到n,边编号从0到m-1
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define INF 1e9
using namespace std;
const int maxn=1050;//之前这里只写10+5,一直TLE,真是悲剧

struct Edge
{
Edge(){}
Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}
int from,to,cap,flow;
};

struct Dinic
{
int n,m,s,t; //结点数,边数(包括反向弧),源点与汇点编号
vector<Edge> edges; //边表 edges[e]和edges[e^1]互为反向弧
vector<int> G[maxn]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[maxn]; //BFS使用,标记一个节点是否被遍历过
int d[maxn]; //d[i]表从起点s到i点的距离(层次)
int cur[maxn]; //cur[i]表当前正访问i节点的第cur[i]条弧

void init(int n,int s,int t)
{
this->n=n,this->s=s,this->t=t;
for(int i=0;i<=n;i++) G[i].clear();
edges.clear();
}

void AddEdge(int from,int to,int cap)
{
edges.push_back( Edge(from,to,cap,0) );
edges.push_back( Edge(to,from,0,0) );
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}

bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int> Q;//用来保存节点编号的
Q.push(s);
d[s]=0;
vis[s]=true;
while(!Q.empty())
{
int x=Q.front(); Q.pop();
for(int i=0; i<G[x].size(); i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to] && e.cap>e.flow)
{
vis[e.to]=true;
d[e.to] = d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}

//a表示从s到x目前为止所有弧的最小残量
//flow表示从x到t的最小残量
int DFS(int x,int a)
{
if(x==t || a==0)return a;
int flow=0,f;//flow用来记录从x到t的最小残量
for(int& i=cur[x]; i<G[x].size(); i++)
{
Edge& e=edges[G[x][i]];
if(d[x]+1==d[e.to] && (f=DFS( e.to,min(a,e.cap-e.flow) ) )>0 )
{
e.flow +=f;
edges[G[x][i]^1].flow -=f;
flow += f;
a -= f;
if(a==0) break;
}
}
return flow;
}

int Maxflow()
{
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow += DFS(s,INF);
}
return flow;
}
}DC;

int a[1500];
int b[20];
int main()
{
//总共有1024中方案 对应结点0-1023
int n,m;
int u,v;
while(~scanf("%d%d",&n,&m))
{
//星球对应1031-1040
//源点 1045 汇点1046
DC.init(1050,1045,1046);
memset(a,0,sizeof a);
int t;
for(int i=0;i<n;i++)
{
t=0;
for(int j=0;j<m;j++)
{
scanf("%d",&u);
if(u)
t+=(1<<j);
}
a[t]++;
}
for(int i=0;i<m;i++)
{
scanf("%d",&b[i]);
DC.AddEdge(1030+1+i,1046,b[i]);
}
int len=(1<<m)-1;

for(int i=0;i<=len;i++)
{
if(a[i])
{
t=i;
v=1;
DC.AddEdge(1045,i,a[i]);
while(t)
{
if(t&1)
{
DC.AddEdge(i,1030+v,INF);
}
v++;
t=t>>1;
}
}
}
if(DC.Maxflow()==n)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: