题目链接
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;
}