HDU - 3605 Escape 状态压缩最大流

题目链接

https://vjudge.net/problem/HDU-3605

题意

n个人(范围10w),m个星球(范围10),每个人有对星球的不同喜好,每个星球有自己的容量,问能否让所有人都在满意的星球上?

思路

一眼就是最大流(二分匹配我没学。。),朴素做法s向人连边权1,人向感兴趣星球连边权1,星球向t连边权为容量,跑dinic,一遍TLE,芜湖~

之前学网络流的时候知道Dinic上界很糟糕,但是大部分题不卡,今天一查才知道上界是O(V²E),换了ISAP的板子也不行,百度一下得知需要状态压缩。

最多只有10个星球,那么对星球的喜好也就最多只有1024种组合。最多10w人,他们除了喜好不同外,没有任何别的信息,所以完全可以用组合代替人做节点。先统计不同状态的人数,s向状态连边,权为状态人数,状态向星球连边,权为人数,星球向t连边,权容量,这样就能将V从1e5压缩到1e3,E也从1e6到了1e4的级别。

时限2s,状压Dinic跑了1.2s。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
using namespace std;
	typedef long long ll;
	const int inf=0x3f3f3f3f;
	const int maxn=100100;
	const int maxe=2500010;

	int head[maxn],cnt;
	struct Edge{
		int v;
		int w;
		int next;
	}edge[maxe];

	int n,m,s,t;
	ll maxflow;
	int deep[maxn];
	int now[maxe];


	void init(){
		memset(head,-1,sizeof(head));
		cnt=0;
		maxflow=0; 
		return ;	
	}
	void add(int u,int v,int w){
  		//cout<<u<<" "<<v<<" "<<w<<endl;
		edge[cnt].v=v;
		edge[cnt].w=w;
		edge[cnt].next=head[u];
		head[u]=cnt++;
	}

	inline bool bfs(){
    	memset(deep,0x3f,sizeof(deep));
    	queue<int>q;
    	q.push(s);deep[s] = 0;now[s] = head[s];
    	while(q.size()){
        	int x = q.front();q.pop();
        	for(int i=head[x];i!=-1;i=edge[i].next){
        	    int y=edge[i].v;
         	    if(edge[i].w>0&&deep[y]==inf){
         	    	q.push(y);
        	        now[y]=head[y];
         	       	deep[y]=deep[x]+1;
        	        if(y==t)	return 1;
				}
        	}
    	}
    	return 0;
	}

	ll dfs(int x,int flow){
    	if(x==t)	return flow;
    	ll ans = 0,k,i;
    	for(i=now[x];i!=-1&&flow;i=edge[i].next){
        	now[x]=i;
        	int y=edge[i].v;
        	if(edge[i].w>0&&(deep[y]==deep[x]+1)){
        	    k=dfs(y,min(flow,edge[i].w));
         		if(!k)	deep[y]=inf;
            	edge[i].w-=k;
            	edge[i^1].w+=k;
            	ans+=k;
            	flow-=k;
        	}
    	}
    	return ans;
	}	

	void dinic(){
    	while(bfs())
    	    maxflow+=dfs(s,inf);
	}
	int sta[5000];
	int main(){	
		while(scanf("%d%d",&n,&m)==2){
			memset(sta,0,sizeof sta);
			s=5000,t=s+1;
			init();
			for(int i=1;i<=n;i++){
				int tmp=0,fg;
				for(int j=1;j<=m;j++){
					scanf("%d",&fg);
					if(fg){
						tmp|=(1<<(j-1));
					}
				}
				sta[tmp]++;
			}
			for(int i=1;i<(1<<m);i++){
				if(sta[i]){
					add(s,i,sta[i]);
					add(i,s,0);
					for(int j=1;j<=m;j++){
						if(i&(1<<(j-1))){
							add(i,2000+j,sta[i]);
							add(2000+j,i,0);
						}
					}
				}
			}
			for(int i=1;i<=m;i++){
				int w;
				scanf("%d",&w);
				add(2000+i,t,w);
				add(t,2000+i,0);
			}
			dinic();
			if(maxflow==n)
				printf("YES\n");
			else
				printf("NO\n");
		}
    	return 0;
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值