HDU 3605 Escape(最大流+状压缩点 / 二分图多重匹配)

18 篇文章 0 订阅
15 篇文章 0 订阅

https://vjudge.net/problem/HDU-3605

这道题直接最大流就爆了,蠢的我没算复杂度,后来回过神时间复杂度是O(V^2*E)...\

百度正确做法:状压缩点,因为最多只有十个星球,对于每个人他都有至多1024(2^10)种可能的选择,因此可以把这1e5个点缩成1024个点,点权重复的用从源点到该点的流量进行管理,有几个点就设置流量为几,啊好神奇,顺便学一下二分图多重匹配吧(好像没什么用,不就是最大流??)

二分图多重匹配

适用于二分图,多重匹配的问题,匈牙利算法

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#define ll long long
#define mod 1000000007
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 100005;
const int maxm = 15;
bool vis[maxm];
int g[maxn][maxm];
int link[maxm][maxn];
int num[maxm];
int n, m;
bool dfs(int u)
{
    for(int v = 1; v <= m; v ++)
    {
        if(g[u][v] && ! vis[v])
        {
            vis[v] = 1;
            if(link[v][0] < num[v])
            {
                link[v][++ link[v][0]] = u;
                return 1;
            }
            for(int i = 1; i <= num[v]; i ++)
            {
                if(dfs(link[v][i]))
                {
                    link[v][i] = u;
                    return 1;
                }
            }
        }
    }
    return 0;
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++)
                scanf("%d", &g[i][j]);
        for(int i = 1; i <= m; i ++)
            scanf("%d", &num[i]);
        bool flag = 1;
        memset(link, 0, sizeof(link));
        for(int u = 1; u <= n; u ++)
        {
            memset(vis, 0, sizeof(vis));
            if(! dfs(u))
            {
                flag = 0;
                break;
            }
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

最大流+缩点

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#define ll long long
#define mod 1000000007
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 2005;
const int maxm = 15;
int cnt, st, en;
int n, m;
int N;
int u, v, w;
int num[maxn];
struct node
{
    int to, next, w;
}e[maxn*maxm];
int head[maxn], depth[maxn], cur[maxn];
void init()
{
    //st = 1, en = n;
    cnt = 0;
    memset(head, -1, sizeof(head));
    memset(num, 0, sizeof(num));
}
void addedge(int u, int v, int w)
{
    e[cnt].w = w;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt ++;

    e[cnt].w = 0;
    e[cnt].to = u;
    e[cnt].next = head[v];
    head[v] = cnt ++;
}
int dfs(int u, int flow)
{
    if(u == en) return flow;
    for(int& i = cur[u]; ~i; i = e[i].next)
    {
        if(depth[e[i].to] == depth[u] + 1 && (e[i].w != 0))
        {
            int zen = dfs(e[i].to, min(e[i].w, flow));
            if(zen)
            {
                e[i].w -= zen;
                e[i^1].w += zen;
                return zen;
            }
        }
    }
    return 0;
}
int bfs()
{
    queue<int> q;
    while(! q.empty()) q.pop();
    memset(depth, 0, sizeof(depth));
    depth[st] = 1;
    q.push(st);
    while(! q.empty())
    {
        int h = q.front();
        q.pop();
        for(int i = head[h]; ~i; i = e[i].next)
        {
            if((! depth[e[i].to]) && e[i].w)
            {
                depth[e[i].to] = depth[h] + 1;
                q.push(e[i].to);
            }
        }
    }
    if(depth[en]) return 1;
    return 0;
}
int dinic()
{
    int ans = 0;
    while(bfs())
    {
        for(int i = 0; i <= N; i ++)
            cur[i] = head[i];
        while(int d = dfs(st, inf))
            ans += d;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        init();
        int w;
        int tmp;
        for(int i = 1; i <= n; i ++)
        {
            tmp = 0;
            for(int j = 1; j <= m; j ++)
            {
                scanf("%d", &w);
                tmp = tmp * 2 + w;
            }
            num[tmp] ++;
        }
        tmp = 0;
        for(int i = 1; i <= 1024; i ++)
        {
            if(num[i])
                tmp ++;
        }
        N = tmp + m + 1;
        st = 0, en = N;
//        for(int i = 1; i <= tmp; i ++)
//            addedge(0, i, num[i]);
        int now = 0;
        for(int i = 1; i <= 1024; i ++)
        {
            if(num[i])
            {
                now ++;
                addedge(0, now, num[i]);
                int top = m;
                int x = i;
                while(x)
                {
                    if(x & 1) addedge(now, tmp+top, inf);
                    top --;
                    x /= 2;
                }
            }
        }
        int tmp1;
        for(int i = 1; i <= m; i ++)
        {
            scanf("%d", &tmp1);
            addedge(tmp + i, N, tmp1);
        }
        //cout<<st<<' '<<tmp<< ' '<<en<<endl;
        int ans = dinic();
        if(ans >= n) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值