题意:
n个人,m个星球,输入一个n*m的01矩阵,0表示第i个人不居住在第j个星球,反之居住。接下来m个数表示第i个星球最多居住多少个人。求是否能够让所有人都居住在星球上
题解:
一开始以为是最大流的模版题,直接套模版,发现TLE了,因为人数是10e5,而m最大只有10,最多有2^10即1024中状态。所以对居住星球进行状态压缩,即每个状态有多少人居住,源点与状态连边,容量是该状态的人数,然后通过状态找出原星球,状态与星球建立边,容量是状态的人数,最后星球与会点建立边,容量是星球最大居住人数。
#include <bits/stdc++.h>
using namespace std;
/*ISAP邻接表形式
*/
const int maxn=1100;//点数的最大值
const int maxm=1100;//边数的最大值
const int inf=0x3f3f3f3f;
template <class T>
inline void scan_d(T &ret)
{
char c;
ret = 0;
while ((c = getchar()) < '0' || c > '9');
while (c >= '0' && c <= '9')
{
ret = ret * 10 + (c - '0'), c = getchar();
}
}
struct Edge {
int to,next,cap,flow;
}edge[maxn*maxn*4];//注意是maxm
int tol;
int head[maxn];
int cur[maxn],d[maxn];// 当前弧下标 结点到汇点距离下界
int p[maxn],gap[maxn];//可增广路上的上一条弧 gap优化 //比dinic多的两个数组
void init() {
tol=0;
memset(head,-1,sizeof(head));
}
//加边,单向图三个参数,双向图四个参数
void addedge(int u,int v,int w,int rw=0) {
edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u];
edge[tol].flow=0; head[u]=tol++;
edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v];
edge[tol].flow=0; head[v]=tol++;
}
//输入参数:起点、终点、点的总数
//点的编号没有影响,只要输入点的总数
int sap(int s,int t,int N){
memset(gap, 0, sizeof(gap));
memset(d, 0, sizeof(d));
memcpy(cur, head, sizeof(head));
int u=s;
p[u]=-1;
gap[0]=N;
int ans=0;
while(d[s]<N){
if(u == t){
int Min=inf;
for(int i=p[u]; i!=-1; i=p[edge[i^1].to])//找最小残量值
if(Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
for(int i = p[u]; i!=-1; i=p[edge[i^1].to]){//增广
edge[i].flow+=Min;
edge[i^1].flow-=Min;
}
u=s;
ans+=Min;
continue;
}
bool ok=false;
int v;
for(int i=cur[u]; i!=-1; i=edge[i].next){
v=edge[i].to;
if(edge[i].cap-edge[i].flow && d[v]+1==d[u]){//Advance前进
ok=true;
cur[u]=p[v]=i;
break;
}
}
if(ok){
u=v;
continue;
}
//Retreat走不动了,撤退
int Min=N;
for(int i=head[u]; i!=-1; i=edge[i].next)
if(edge[i].cap-edge[i].flow && d[edge[i].to] < Min){
Min=d[edge[i].to];
cur[u]=i;
}
gap[d[u]]--;
if(!gap[d[u]])return ans;
d[u] = Min+1;
gap[d[u]]++;
if(u!=s) u=edge[p[u]^1].to;//退一步,沿父边返回
}
return ans;
}
int main(){
int n,m,c,cnt,tt;
int temp[maxm];
while(scanf("%d%d",&n,&m)!=EOF){
init();
int source = 0;
int sink = (1<<m)-1+m+1;
memset(temp,0,sizeof(temp));
for(int i=1;i<=n;i++){
cnt = 0;
tt = 1;
for(int j=1;j<=m;j++){
scan_d(c);
if(c)
cnt |= (1<<(j-1));
}
//cout<<cnt<<endl;
temp[cnt]++;
}
for(int i=1;i<(1<<m);i++){
addedge(source, i, temp[i]);
// cout<<source<<" "<<i<<" "<<temp[i]<<endl;
int ii = i;
int k = 0;
while(ii){
k++;
if(ii%2){
addedge(i, (1<<m)-1+k, temp[i]);
//cout<<i<<" "<<k<<endl;
// cout<<i<<" "<<(1<<m)+k<<" "<<temp[i]<<endl;
}
ii/=2;
}
}
for(int i=1;i<=m;i++){
scanf("%d",&c);
addedge((1<<m)-1+i, sink, c);
// cout<<(1<<m)+i<<" "<<sink<<" "<<c<<endl;
}
int dd = sap(source, sink, sink+1);
// cout<<dd<<endl;
if(n==dd){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}