hdu3605 Escape

题意:

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;
}

猜你喜欢

转载自blog.csdn.net/a1561067921/article/details/77946874
今日推荐