hdu 3605 Escape【網路流+狀態壓縮】
阿新 • • 發佈:2018-12-17
有n個人要移居m個星球,給出每個合適的星球,每個星球最多能容納的人數,問是否所有人都可以移居。(1≤N≤105,1≤M≤10)(1≤N≤105,1≤M≤10)
最多有十個星球,而N很大,所以可能會有很多重複,因為每個人去哪個星球最多有2^10中情況,所以可以壓縮點,把情況相同的人放到一塊,在進行網路流就行;
這道題中,要建立超級源點 0 和超級匯點 (多少種情況+m+1);
鄰接表:
#include<cstdio> #include<cstring> #include<string> #include<stdlib.h> #include<iostream> #include<algorithm> #include<math.h> #include<map> #include<vector> #include<stack> #define inf 0x3f3f3f3f #include<queue> using namespace std; typedef long long ll; const int N=1050; struct node { int u,v,flow,cal; }q; vector<node>edge;//記錄邊 vector<int>head[N];//記錄每個邊的下標(可自行模擬) int a[N],pre[N],peo[N]; int n,m; void init(int mm) { edge.clear(); for(int i=0;i<=mm;i++) head[i].clear(); } void add(int u,int v,int flow) { edge.push_back(node{u,v,0,flow}); edge.push_back(node{v,u,0,0}); int tmp=edge.size(); head[u].push_back(tmp-2); head[v].push_back(tmp-1); } int solve(int s,int t)//EK演算法 { int ans=0; queue<int>que; while(true) { que.push(s); memset(a,0,sizeof(a)); a[s]=inf; while(!que.empty()) { int u=que.front(); que.pop(); for(int k=0;k<head[u].size();k++) { q=edge[head[u][k]]; if(!a[q.v]&&q.cal>q.flow) { que.push(q.v); pre[q.v]=head[u][k]; a[q.v]=min(a[u],q.cal-q.flow); } } } if(!a[t]) break; for(int v=t;v!=s;v=edge[pre[v]].u) { edge[pre[v]].flow+=a[t]; edge[pre[v]^1].flow-=a[t]; } ans+=a[t]; } if(ans==n) return true; return false; } int main() { while(~scanf("%d %d",&n,&m)) { memset(peo,0,sizeof(peo)); int temp,p,po=0; for(int i=0;i<n;i++) { temp=0; for(int j=0;j<m;j++) { temp<<=1; scanf("%d",&p); temp+=p;//二進位制統計情況是否相同 } if(!peo[temp]) po++;//記錄最多有多少情況 peo[temp]++; } int t=po+m+1;//匯點,即所有星球連結一個超級匯點 init(t); for(int i=1;i<=m;i++) { scanf("%d",&p); add(po+i,t,p);//建立從星球到匯點的邊 } p=0; for(int i=0;i<(1<<m);i++) { if(peo[i]) { add(0,++p,peo[i]);//建立源點到每種情況的邊 int tmp=i,f=1; while(tmp) { if(tmp&1) add(p,po+f,peo[i]);//建立每種情況到它們可以去的星球的邊 f++; tmp>>=1; } } } if(solve(0,t)) printf("YES\n"); else printf("NO\n"); } return 0; }