您的位置:首页 > 其它

hdu 3605 Escape(最大流+状态压缩 or 二分图多重匹配)

2015-10-23 13:19 483 查看

Escape

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 7789 Accepted Submission(s): 1684



[align=left]Problem Description[/align]
2012 If this is the end of the world how to do? I do not know how. But now scientists have found that some stars, who can live, but some people do not fit to live some of the planet. Now scientists want your help, is to determine
what all of people can live in these planets.

[align=left]Input[/align]
More set of test data, the beginning of each data is n (1 <= n <= 100000), m (1 <= m <= 10) n indicate there n people on the earth, m representatives m planet, planet and people labels are from 0. Here are n lines, each line represents
a suitable living conditions of people, each row has m digits, the ith digits is 1, said that a person is fit to live in the ith-planet, or is 0 for this person is not suitable for living in the ith planet.

The last line has m digits, the ith digit ai indicates the ith planet can contain ai people most..

0 <= ai <= 100000

[align=left]Output[/align]
Determine whether all people can live up to these stars

If you can output YES, otherwise output NO.

[align=left]Sample Input[/align]

1 1
1
1

2 2
1 0
1 0
1 1


[align=left]Sample Output[/align]

YES
NO
题意:有N个人要去M个星球避难,现在给你每个人适合居住的星球以及每个星球可以容纳的人数,问你这N个人能不能全部安全避难。
状态压缩,最大流。也可以用二分图多重匹配,代码更简单
参考大神分析:
由于N最大为100000,若直接建边的话,网络流的任何算法都承受不起这个时间复杂度。因为M个数最多为10,
利用状态来表示的话最坏只有2^10 = 1024个点,我们可以利用状态压缩的方法来优化时间复杂度。
建图:设置超级源点S,超级汇点T,并记录每个状态出现的次数。
1,S向所有状态建边,容量为该状态出现的次数;
2,所有状态向该状态里面存在的星球建边,容量为无穷大,表示S的流量可以无限到该星球;
3,对流入T的流量加限制,每个星球向T建边,容量为星球可以容纳的人数。
最后跑一次最大流,看是否满流就可以了
*/

//网络流
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
#define NN 100000+100
#define MM 20
int dis[NN],head[NN],cur[NN],rl[NN];
int S,T,cnt,n,m;
struct node{
int v,next,w;
}mp[NN*4];
void add(int u,int v,int w){
mp[cnt].v=v;
mp[cnt].w=w;
mp[cnt].next=head[u];
head[u]=cnt++;
mp[cnt].v=u;
mp[cnt].w=0;
mp[cnt].next=head[v];
head[v]=cnt++;
}
int bfs(){
memset(dis,-1,sizeof(dis));
queue<int> Q;
dis[S]=0;
Q.push(S);
while(!Q.empty()){
int u=Q.front();
Q.pop();
for(int i=head[u];i!=-1;i=mp[i].next){
int v=mp[i].v;
if(dis[v]==-1 && mp[i].w){
dis[v]=dis[u]+1;
if(v==T) return 1;
Q.push(v);
}
}
}
return 0;
}
int dfs(int u,int flow){
if(u==T || flow==0) return flow;
int i,a,ans=flow;
for(int &i=cur[u];i!=-1;i=mp[i].next){
int v=mp[i].v;
if( dis[v]==dis[u]+1 && mp[i].w && (a=dfs(v,min(ans,mp[i].w))) ){
mp[i].w-=a;
mp[i^1].w+=a;
ans-=a;
if(ans==0) return flow;
}
}
return flow-ans;
}
int dinic(){
int ans=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
ans+=dfs(S,0x3f3f3f3f);
}
return ans;
}
int main(){
int a,sum,k;
while(~scanf("%d%d",&n,&m)){
//每个星球都有选和不选两种状态,所以最多有2^10=1024种状态,每种状态作为一个点最多1024个点
//再加星球有10个,所以 S=1024+10+1,T=S+1。
S=1035; T=S+1; sum=cnt=k=0;
memset(head,-1,sizeof(head));
memset(rl,0,sizeof(rl));
for(int i=0;i<n;++i){
int temp=0;
for(int j=0;j<m;++j){
scanf("%d",&a);
if(a)  temp += 1<<j;//记录状态
}
rl[temp]++;//存该种状态出现的次数
}
for(int i=0;i<(1<<m);++i){//每种状态遍历
if(rl[i]){
add(S,i,rl[i]);
for(int j=0;j<m;++j){
if(i & (1<<j)) ////这个式子表示i那个位置的值是不是1,按位与运算 
add(i,1025+j,0x3f3f3f3f);//星球的编号为1025+j
}
}
}
for(int i=0;i<m;++i){
scanf("%d",&a);
add(1025+i,T,a);
}
k=dinic();
if(k==n)
printf("YES\n",k);
else
printf("NO\n");
}
return 0;
}
//二分图多重匹配
#include<stdio.h>
#include<string.h>
#define M 100000+100
#define N 20
int mp[M]
;//i适合在j上生存
int link
[M];//link[i][j]表示i星球住了j个人
int vis
,cap
,num
;//表示星球的容量,num表示星球上的人数
int n,m;
int dfs(int x){
for(int i=0;i<m;i++){
if(!vis[i] && mp[x][i]){
vis[i]=1;
if(num[i]<cap[i]){//如果小于星球最大容量,就把这个人加进去
link[i][num[i]++]=x;
return 1;
}
for(int j=0;j<num[i];j++){
if(dfs(link[i][j])){//查找增广路
link[i][j]=x;
return 1;
}
}
}
}
return 0;
}
int main(){
while(scanf("%d %d",&n,&m) == 2){
int ok=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
scanf("%d",&mp[i][j]);
}
}
for(int i=0;i<m;i++)
scanf("%d",&cap[i]);
memset(num,0,sizeof(num));
for(int i=0;i<n;i++){
memset(vis,0,sizeof(vis));
if(!dfs(i)){
ok=1;
break;
}
}
if(ok) printf("NO\n");
else printf("YES\n");
}
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: