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

题目链接: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;
}


你可能感兴趣的:(HDU 3605 Escape(网络流 + 状压简化 | 二分图多重匹配))