zoukankan      html  css  js  c++  java
  • UVa 二分图匹配 Biginners

    UVa 1045 - The Great Wall Game 最小权匹配


    题意:给你一个n*n的棋盘,上面有n个棋子,要求通过移动各个棋子使得棋子在同一行或者同一列或者对角线上,求最小移动次数。

    思路:直接对于所有可能情况构造二分图,X集合为最初棋子,Y集合为移动后的棋子方位,边权为移动的次数。然后KM算法求最小权匹配。

    /* **********************************************
    Author      : JayYe
    Created Time: 2013-8-18 15:55:41
    File Name   : zzz.cpp
     *********************************************** */
    
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    
    int max(int a, int b) { return a>b?a:b; }
    int min(int a, int b) { return a>b?b:a; }
    
    const int maxn = 22;
    
    struct PP {
        int x, y;
    }a[maxn];
    
    int n, slack[maxn], lx[maxn], ly[maxn], match[maxn], w[maxn][maxn];
    bool S[maxn], T[maxn];
    
    bool dfs(int i) {
        S[i] = 1;
        for(int j = 1;j <= n; j++) if(!T[j])
            slack[j] = min(slack[j], w[i][j] - lx[i] - ly[j]);
        for(int j = 1;j <= n; j++) if(w[i][j] == lx[i] + ly[j] && !T[j]) {
            T[j] = 1;
            if(!match[j] || dfs(match[j])) {
                match[j] = i;
                return true;
            }
        }
        return false;
    }
    
    void update() {
        int delta = 1<<22;
        for(int i = 1;i <= n; i++) if(!T[i])
            delta = min(delta, slack[i]);
        for(int i = 1;i <= n; i++) {
            if(S[i])    lx[i] += delta;
            if(T[i])    ly[i] -= delta;
        }
    }
    
    void KM() {
        int i, j;
        for(i = 1;i <= n; i++) {
            ly[i] = match[i] = 0;
            lx[i] = 1<<22;
            for(j = 1;j <= n; j++)
                lx[i] = min(lx[i], w[i][j]);
        }
        for(i = 1;i <= n; i++) {
            while(true) {
                for(j = 1;j <= n; j++) S[j] = T[j] = 0, slack[j] = 1<<22;
                if(dfs(i))  break;
                else    update();
            }
        }
    }
    
    int solve() {
        int i, j, k;
    
        for(i = 1;i <= n; i++)
            scanf("%d%d" ,&a[i].x, &a[i].y);
        int ans = 1<<22;
        // 棋子在同一行的情况
        for(i = 1;i <= n; i++) {
            for(j = 1;j <= n; j++)
                for(k = 1;k <= n; k++)
                    w[j][k] = abs(i - a[j].x) + abs(k - a[j].y);
            KM();
            int cur = 0;
            for(j = 1;j <= n; j++)  cur += lx[j] + ly[j];
            ans = min(ans, cur);
        }
        // 棋子在同一列的情况
        for(i = 1;i <= n; i++) {
            for(j = 1;j <= n; j++)
                for(k = 1;k <= n; k++)
                    w[j][k] = abs(k - a[j].x) + abs(i - a[j].y);
            KM();
            int cur = 0;
            for(j = 1;j <= n; j++)  cur += lx[j] + ly[j];
            ans = min(ans, cur);
        }
        // 棋子在对角线的两种情况
        for(i = 1;i <= n; i++)
            for(j = 1;j <= n; j++)
                w[i][j] = abs(j - a[i].x) + abs(j - a[i].y);
        KM();
        int cur = 0;
        for(i = 1;i <= n; i++)  cur += lx[i] + ly[i];
        ans = min(ans, cur);
    
        for(i = 1;i <= n; i++)
            for(j =1;j <= n; j++)
                w[i][j] = abs(j - a[i].x) + abs(n-j+1 - a[i].y);
        KM();
        cur = 0;
        for(i = 1;i <= n; i++)  cur += lx[i] + ly[i];
        ans = min(ans, cur);
        return ans;
    }
    
    int main() {
        int cas = 1;
        while(scanf("%d", &n) != -1  && n) {
            printf("Board %d: %d moves required.
    
    ", cas++, solve());
        }
        return 0;
    }
    


    UVa 12168 - Cat vs. Dog 最大独立集


    根据题意直接构造二分图,X集合表示喜欢狗的人,Y集合表示喜欢猫的人,如果X集合里的人不喜欢某只猫,就与Y集合里喜欢该猫的人连边,反之也一样。那么要使得尽可能多的人满足愿望,也就是求最大独立集。

    最大独立集 = N - 最大匹配


    /* **********************************************
    Author      : JayYe
    Created Time: 2013-8-18 16:53:58
    File Name   : zzz.cpp
    *********************************************** */
    
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 500+10;
    struct PP {
        int x, y;
        PP() {}
        PP(int x, int y) : x(x), y(y) {}
    }cat[maxn], dog[maxn];
    
    int n, m, match[maxn];
    bool vis[maxn], mp[maxn][maxn];
    
    bool dfs(int i) {
        for(int j = 1;j <= m; j++) if(mp[i][j] && !vis[j]) {
            vis[j] = 1;
            if(!match[j] || dfs(match[j])) {
                match[j] = i;
                return true;
            }
        }
        return false;
    }
    
    int main() {
        int i, j, t, c, d;
        scanf("%d", &t);
        while(t--) {
            scanf("%d%d%d", &c, &d, &n);
            int n1 = 0, n2 = 0, x, y;
            for(i = 1;i <= n; i++) {
                char ch1[11], ch2[11];
                scanf("%s%s", ch1, ch2);
                if(ch1[0] == 'C') {
                    sscanf(ch1+1, "%d", &x);
                    sscanf(ch2+1, "%d", &y);
                    cat[++n1] = PP(x, y);
                }
                else {
                    sscanf(ch1+1, "%d", &x);
                    sscanf(ch2+1, "%d", &y);
                    dog[++n2] = PP(x, y);
                }
            }
            n = n1, m = n2;
            if(n == 0 || m == 0) {
                printf("%d
    ", n+m); continue;
            }
            for(i = 1;i <= n; i++) {
                for(j = 1;j <= m; j++) {
                    if(cat[i].y == dog[j].x || cat[i].x == dog[j].y)
                        mp[i][j] = 1;
                    else
                        mp[i][j] = 0;
                }
            }
            for(i = 1;i <= m; i++)  match[i] = 0;
            int ans = 0;
            for(i = 1;i <= n; i++) {
                for(j = 1;j <= m; j++)  vis[j] = 0;
                if(dfs(i))  ans++;
            }
            printf("%d
    ", n+m - ans);
        }
        return 0;
    }
    


    Uva 1349 - Optimal Bus Route Design


    题意:给你一个n个点的有向带环图,要你找出几个圈使得每个结点只属于一个圈,要求输出最小的总的长度,如果没有这样的方案,输出N。


    构造二分图,把所有的结点拆成两个,放在X集合的为i, 放在Y集合的为 i ',如果有边i -> j,则在图中引入边i -> j',这样子构造好后实际上就是求最小权完美匹配,如果没有完美匹配则无解。

    这里需要注意的是输入的边可能有好多条是重复的但是权值不同,需要取最小权,这个wa了我好几发。。。


    /* **********************************************
    Author      : JayYe
    Created Time: 2013-8-18 17:31:11
    File Name   : zzz.cpp
     *********************************************** */
    
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    
    int max(int a, int b) { return a>b?a:b; }
    int min(int a, int b) { return a>b?b:a; }
    
    const int INF = 1<<22;
    const int maxn = 100+10;
    int n, slack[maxn], match[maxn], lx[maxn], ly[maxn], w[maxn][maxn];
    bool S[maxn], T[maxn];
    
    bool dfs(int i) {
        S[i] = 1;
        for(int j = 1;j <= n; j++) if(!T[j])
            slack[j] = min(slack[j], w[i][j] - lx[i] - ly[j]);
        for(int j = 1;j <= n; j++) if(w[i][j] == lx[i] + ly[j] && !T[j]) {
            T[j] = 1;
            if(!match[j] || dfs(match[j])) {
                match[j] = i;
                return true;
            }
        }
        return false;
    }
    
    void update() {
        int delta = INF;
        for(int i = 1;i <= n; i++) if(!T[i])
            delta = min(delta, slack[i]);
        for(int i = 1;i <= n; i++) {
            if(S[i])    lx[i] += delta;
            if(T[i])    ly[i] -= delta;
        }
    }
    
    void KM() {
        int i, j;
        for(i = 1;i <= n; i++) {
            ly[i] = match[i] = 0;
            lx[i] = INF;
            for(j = 1;j <= n; j++)
                lx[i] = min(lx[i], w[i][j]);
        }
    
        for(i = 1;i <= n; i++) {
            while(true) {
                for(j = 1;j <= n; j++)  S[j] = T[j] = 0, slack[j] = INF;
                if(dfs(i))  break;
                else    update();
            }
        }
    }
    
    void solve() {
        int i, j;
        for(i = 1;i <= n; i++)
            for(j = 1;j <= n; j++)
                w[i][j] = INF;
        for(i = 1;i <= n; i++) {
            while(scanf("%d", &j) && j) {
                int dis;
                scanf("%d", &dis);
                // 同一条路要取最小值
                w[i][j] = min(w[i][j], dis);
            }
        }
        KM();
        int ans = 0;
        for(i = 1;i <= n; i++)  ans += lx[i] + ly[i];
        if(ans > INF-10)   puts("N");
        else    printf("%d
    ", ans);
    }
    
    int main() {
        while(scanf("%d", &n) != -1 && n) {
            solve();
        }
        return 0;
    }
    



  • 相关阅读:
    Spring整合JMS(一)——基于ActiveMQ实现 (转)
    spring mvc入门配置
    Java enum的用法详解 (转)
    php学习日记7(关于“ javascript : ; ”的意思)
    php学习日记6(关于php中json_decode和json_encode的区别)
    php学习日记5(关于php中getCacheAll()方法的使用)
    php学习日记4(关于php中的$this->assign(); 与 $this->display();)
    php学习日记3(关于Mysql中limit用法)
    php学习日记2(关于php中的符号->、=>和::的意思)
    php学习日记1(关于Thinkphp框架中D的含义和用法)
  • 原文地址:https://www.cnblogs.com/riskyer/p/3266659.html
Copyright © 2011-2022 走看看