HDU - 3605 (最大流 OR 二分图最大匹配)

http://acm.hdu.edu.cn/showproblem.php?pid=3605

题意:

n个人类移民到m个外星,每个人都有喜欢的星星,但星星有最大人数限制,问能不能,让每个人都移民到喜欢的星球

思路:

可用最大流也可用二分图最大匹配

1。二分图匹配

匈牙利算法的一个变形,把一对一变成一对多

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 100005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007

int n, m;
int link[maxn][15], star[15], sta[15][maxn], used[15], have[15];

bool find(int x) {
    for (int i = 1; i <= m; i ++) {
        if(!link[x][i] || used[i]) continue;//如果你不喜欢或者前面已经的人已经选过了
        used[i] = 1;
        if(have[i] < star[i]) {
            sta[i][++have[i]] = x;
            return 1;
        }else{
            for (int j = 1; j <= have[i]; j ++)
                if(find(sta[i][j])) {
                    sta[i][have[i]] = x;
                    return 1;
                }
        }
    }
    return 0;
}

int main(int argc, const char * argv[]) {
    while(scanf("%d %d", &n, &m) != EOF){
        memset(have, 0, sizeof(have));
        for (int i = 1; i <= n; i ++)
            for (int j = 1; j <= m; j ++)
                scanf("%d", &link[i][j]);
        for (int i = 1; i <= m; i ++)
            scanf("%d", &star[i]);
        int i;
        for (i = 1; i <= n; i ++) {
            memset(used, 0, sizeof(used));
            if(!find(i))
                break;
        }
        if(i > n) printf("YES\n");
        else printf("NO\n");


    }
    return 0;
}

2.最大流

如果按照一般的方法会超时,但是星球的数量≤10,这时候可以用状压来建边,因为只有10个星球,那么状态一共只有2^10(1024) 种,可以用源点S 跟状态建边,权值为状态的数量
状态与星球建边,权值是状态的数量,星球与汇点T建边,权值是星球的容量
状态1->2^10,星球2^10+1->2^1+10

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 8000005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007

struct Edge{
    int v, w, nxt;
}edge[maxn];

int n, m;
int S, T, head[200005], cur[200005], dis[200005], gap[200005], aug[200005], pre[200005], a[2500], tot;

void init(){
    memset(head, -1, sizeof(head));
    tot = 0 ;
}

void addEdge(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot ++;

    edge[tot].v = u;
    edge[tot].w = 0;
    edge[tot].nxt = head[v];
    head[v] = tot ++;
}

int SAP(int n) {
    int max_flow = 0, u = S, v;
    int id, minDis;
    aug[S] = INF;
    pre[S] = -1;
    memset(dis, 0, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    gap[0] = n;
    for (int i = 0; i <= n; i ++)
        cur[i] = head[i];
    while(dis[S] < n) {
        int flag = 0;
        if(u == T) {
            max_flow += aug[T];
            for (v = pre[T]; v + 1; v = pre[v]) {
                id = cur[v];
                edge[id].w -= aug[T];
                edge[id ^ 1].w += aug[T];
                aug[v] -= aug[T];
                if(edge[id].w == 0)
                    u = v;
            }
        }
        for (int i = cur[u]; i + 1; i = edge[i].nxt) {
            v = edge[i].v;
            if(edge[i].w > 0 && dis[u] == dis[v] + 1) {
                flag = 1;
                pre[v] = u;
                cur[u] = i;
                aug[v] = min(aug[u], edge[i].w);
                u = v;
                break;
            }
        }
        if(!flag) {
            if(--gap[dis[u]] == 0)
                break;
            minDis = n;
            cur[u] = head[u];
            for (int i = head[u]; i + 1; i = edge[i].nxt) {
                v = edge[i].v;
                if(edge[i].w > 0 && dis[v] < minDis) {
                    minDis = dis[v];
                    cur[u] = i;
                }
            }
            dis[u] = minDis + 1;
            gap[dis[u]] ++;
            if(u != S)
                u = pre[u];
        }
    }
    return max_flow;
}

int main(int argc, const char * argv[]) {
    while(scanf("%d %d", &n, &m) != EOF){
        init();
        S = 0; T = (1 << (m + 1)) + m + 1;
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= n; i ++) {
            int sta = 0;
            for (int j = 1, v; j <= m; j ++) {
                scanf("%d", &v);
                if(v) sta |= (1 << j);
            }
            a[sta] ++;//每种状态的数量
        }
        for (int i = 2; i < (1 << (m + 1)); i ++) {
            if(a[i]) {
                addEdge(S, i, a[i]);//源点跟状态建边
                for (int j = 1; j <= m; j ++)
                    if(i & (1 << j))//如果喜欢j星球
                        addEdge(i, j + (1 << (m + 1)), a[i]);//那么状态跟星球建边

            }
        }
        for (int i = 1, v; i <= m; i ++) {
            scanf("%d", &v);
            addEdge((1 << (m + 1)) + i, T, v);//星球跟汇点T建边
        }
        int d = SAP(T + 1);
        if(d == n) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

你可能感兴趣的:(题解,网络流)