HDU 3605 Escape(网络流 + 状压简化 | 二分图多重匹配)

来源:互联网 发布:看图找地方的软件 编辑:程序博客网 时间:2024/05/21 14:43

题目链接:Click here~~

题意:

有 n 个人选择 m 个星球居住,选择情况和星球居住上限有限制,问是否能全部满足要求。

解题思路:

解法一:

开始想到了最裸的建图,果然 TLE 了。

由于 n 太大(1e5) m 很小(10),所以我们可以进行状态压缩,将 n 个人等价划分成 1 << m 个集合,记录下各个集合有多少人。

建边:S -> mask,cap = cnt[mask] ; mask -> planet of mask,cap = inf;planet -> T,cap = limit of planet。

#include <queue>#include <stdio.h>#include <string.h>#include <algorithm>const int inf = 0x3fffffff;using namespace std;#define CLR(a,v) memset(a,v,sizeof(a))template<int N,int M>struct Isap{    int top,s,t,d[N],pre[N],cur[N],gap[N];    struct Vertex{        int head;    }V[N];    struct Edge{        int v,next;        int c,f;    }E[M];    inline void init(){        CLR(V,-1);        top = 0;    }inline void set_st(int _s,int _t){        s = _s , t = _t;    }    inline void add_edge(int u,int v,int c){        E[top].v = v;        E[top].c = c;E[top].f = 0;        E[top].next = V[u].head;        V[u].head = top++;    }    inline void add(int u,int v,int c){        add_edge(u,v,c);        add_edge(v,u,0);    }    inline void set_d(){        CLR(d,-1);CLR(gap,0);        queue<int> Q;        d[t] = 0;        Q.push(t);        while(!Q.empty()){            int v = Q.front();            Q.pop();            ++gap[ d[v] ];            for(int i=V[v].head;~i;i=E[i].next){                int u = E[i].v;                if(d[u] == -1){                    d[u] = d[v] + 1;                    Q.push(u);                }            }        }    }    int sap(int n){//n vertexs        set_d();        int ans = 0, u = s, flow = inf;        memcpy(cur,V,sizeof(V));        while(d[s] < n){            int &i = cur[u];            for(;~i;i=E[i].next){                int v = E[i].v;       if(E[i].c>E[i].f && d[u]==d[v]+1){                u = v;                pre[v] = i;                flow = min(flow,E[i].c-E[i].f);                    if(u == t){                        while(u != s){                            int j = pre[u];                            E[j].f += flow;                            E[j^1].f -= flow;                            u = E[j^1].v;                        }                        ans += flow;                        flow = inf;                    }                    break;                }            }            if(i == -1){                if(--gap[ d[u] ] == 0)                    break;                int dmin = n - 1;                cur[u] = V[u].head;                for(int j=V[u].head;~j;j=E[j].next)                    if(E[j].c > E[j].f)                        dmin = min(dmin,d[ E[j].v ]);                    d[u] = dmin + 1;                    ++gap[ d[u] ];                    if(u != s)                        u = E[ pre[u]^1 ].v;            }        }        return ans;    }};const int N = 1<<10 | 25;Isap<N,N*11*2+5> g;int cnt[1<<10];inline void In(int& res){    int c;    while((c = getchar())<'0' || c>'9');    res = c-'0';    while((c = getchar())>='0' && c<='9')        res = res*10 + c-'0';}int main(){    int n,m;    while(~scanf("%d%d",&n,&m))    {        CLR(cnt,0);        g.init();        for(int i=1;i<=n;i++)        {            int mask = 0;            for(int j=0;j<m;j++)            {                int can;                //scanf("%d",&can);                In(can);                if(can)                    mask |= 1 << j;            }            cnt[mask]++;        }        int nn = n;        n = 1 << m;        for(int mask=1;mask<n;mask++)            if(cnt[mask])            {                g.add(0,mask,cnt[mask]);                for(int j=0;j<m;j++)                    if(mask & (1 << j))                        g.add(mask,n+j,inf);            }        for(int i=0;i<m;i++)        {            int cap;            //scanf("%d",&cap);            In(cap);            if(cap)                g.add(n+i,n+m,cap);        }        g.set_st(0,n+m);        int ans = cnt[0] ? 0 : g.sap(n+m+1);        puts(ans==nn?"YES":"NO");    }    return 0;}


解法二:二分图多重匹配。应该是裸的吧,之前没做过,模仿别人的写了一个。

#include <vector>#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;const int N = 1e5 + 5;const int M = 12;bool g[N][M],vis[M];int n,m,match[M][N],cap[M],cnt[M];bool dfs(int u){    for(int v=1;v<=m;v++)    {        if(!vis[v] && g[u][v])        {            vis[v] = true;            if(cnt[v] < cap[v])            {                match[v][ ++cnt[v] ] = u;                return true;            }            else            {                for(int i=1;i<=cap[v];i++)                {                    if(dfs(match[v][i]))                    {                        match[v][i] = u;                        return true;                    }                }            }        }    }    return false;}int maxMatch(int n){    memset(match,0,sizeof(match));    memset(cnt,0,sizeof(cnt));    int ans = 0;    for(int i=1;i<=n;i++)    {        memset(vis,false,sizeof(vis));        if(dfs(i))            ++ans;        else            return 0;    }    return ans;}inline void In(int& res){    int c;    while((c = getchar())<'0' || c>'9');    res = c-'0';    while((c = getchar())>='0' && c<='9')        res = res*10 + c-'0';}int main(){    while(~scanf("%d%d",&n,&m))    {        memset(g,false,sizeof(g));        for(int i=1;i<=n;i++)        {            for(int j=1;j<=m;j++)            {                int can;                //scanf("%d",&can);                In(can);                g[i][j] = can;            }        }        for(int i=1;i<=m;i++)        {            //scanf("%d",&cap);            In(cap[i]);        }        puts(maxMatch(n)==n?"YES":"NO");    }    return 0;}