HDU3605 Escape(最大流判满流 + 状压)

【题意】:

有N个人,M个星球,给N*M矩阵,(i, j)为1代表i可以到j星球,0代表不能,问是否能把所有人转移走。

【思路】:

N的范围为1e6,如果让每个人与星球连边一定TLE,再根据矩阵每一行只有0,1可以进行状压,把相同状态idx的人合并到数组siz[idx],

在扫描状态,与符合条件的星球连边。

【建图】:

超级源点sp -> idx  边权siz[idx]

idx -> M  边权inf

M -> 超级汇点tp  边权为容量

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
const int maxm = 10 + 5;
const int inf = 0x3f3f3f3f;
int n, m, d[maxn+maxm], siz[maxn];
int head[maxn+maxm], tot, maxflow;
int sp, tp;
struct edge{
    int to, w, next;
} ed[(maxn*maxm+maxn+maxm)<<1];
inline void init(){
    memset( head ,-1, sizeof(head) );
    memset( siz, 0, sizeof(siz) );
    tot = 1;
}

inline void add( int u, int v, int w ){
    ed[++tot].to = v; ed[tot].w = w; ed[tot].next = head[u]; head[u] = tot;
    ed[++tot].to = u; ed[tot].w = 0; ed[tot].next = head[v]; head[v] = tot;
}

inline bool bfs(){
    memset( d, 0, sizeof(d) );
    queue<int> q;
    d[sp] = 1;
    q.push(sp);
    while( !q.empty() ){
        int x = q.front();
        q.pop();
        for( int i=head[x]; i!=-1; i=ed[i].next ){
            int y = ed[i].to;
            if( ed[i].w && !d[y] ){
                d[y] = d[x] + 1;
                q.push(y);
                if( y==tp ) return 1;
            }
        }
    }
    return 0;
}

inline int dfs( int x, int flow ){
    if( x==tp ) return flow;
    int res = flow, k;
    for( int i=head[x]; i!=-1 && res; i=ed[i].next ){
        int y = ed[i].to;
        if( ed[i].w && d[y]==d[x]+1 ){
            k = dfs( y, min( ed[i].w, res ) );
            if(!k) d[y] = 0;
            ed[i].w -= k;
            ed[i^1].w += k;
            res -= k;
        }
    }
    return flow-res;
}

inline void dinic(){
    int flow = maxflow = 0;
    while( bfs() )
        while( flow=dfs(sp, inf) ) maxflow += flow;
}

int main(){
    // freopen("in.txt", "r", stdin);
    while( ~scanf("%d%d", &n, &m) ){
        init();
        int l = inf, r = -inf;
        for( int i=1; i<=n; i++ ){
            int sum = 0;
            for( int j=1; j<=m; j++ ){
                int tmp;
                scanf("%d", &tmp);
                sum <<= 1;
                sum += tmp;
            }
            siz[sum] ++;
            l = min( l, sum ); r = max( r, sum );
        }
        sp = 0;
        tp = r+m+1;
        for( int i=1; i<=m; i++ ){
            int cont;
            scanf("%d", &cont);
            add( i+r, tp, cont );
        }
        for( int i=l; i<=r; i++ )
            if( siz[i] ){
                int pos = 0;
                while( pos<m ){
                    if( i&(1<<pos) ) add( i, m-pos+r, inf );
                    pos ++;
                }
                add( sp, i, siz[i] );
            }
        dinic();
        if( n<=maxflow ) puts("YES");
        else puts("NO");
    }

    return 0;
}

 

posted @ 2019-06-16 16:34  CoffeeCati  阅读(155)  评论(0编辑  收藏  举报