zoukankan      html  css  js  c++  java
  • Uva

    题意:一个矩形——R*C的网格,在某些位置上有石头,在网格外开一炮可以打掉该行或者该列的石头,求打掉这些石头最少需要多少门大炮,位置分别设在哪行哪列(0<R<1001, 0 < C < 1001)。

    题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2414

    ——>>以每行为X结点,每列为Y结点,有石头的点的横纵坐标关系为边建立二分图,那么,对于一个石头的位置,就是一条边,只要在这条边2个端点中的1个设大炮就可打掉这个石头,这正正是二分图的最小点覆盖。

    对于具体的位置,则可从X中所有的未盖点出发,寻找增广路,并且标记沿途经过的结点,那么X中未被标记的点和Y中被标记的点即为所求,这是因为:

    从X中的未盖点出发,终点一定在X结点(否则存在增广路,与最大匹配矛盾),那么这次增广中标记Y结点的数量就会比标记X结点的数量少1,故取已标记的Y结点用的大炮就会比取已标记的X结点的数量要少1。

    对于X中未被标记的结点a,首先a是已盖点,Y中必有结点b与之匹配,且b一定不和X中的未盖点相连(否则a,b一定被标记了),若a还与Y中的未盖点相连,则应取a,而不是b及与a相连的Y中的未盖点,这样只在a一个点设大炮就可解决掉这几个石头;若a连的还有Y中的已盖点,那么那点由它的匹配点去处理,a不用去处理它;若a不与Y的其它点相连,那么取a点或者取b都行,我取了a点,所以这些情况取已盖点a就是。

    综上可得取X中未被标记的点和Y中被标记的点就是设大炮的行与列。

    另外,用邻接表会比用简单的邻接矩阵快10倍!微笑

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1000 + 10;
    const int maxm = 1000000 + 10;
    
    int R, C, fa[maxn], mo[maxn], v[maxm], head[maxn], nxt[maxm], ecnt;
    bool S[maxn], T[maxn];
    
    void init(){
        memset(head, -1, sizeof(head));
        ecnt = 0;
    }
    
    void addEdge(int uu, int vv){
        v[ecnt] = vv;
        nxt[ecnt] = head[uu];
        head[uu] = ecnt;
        ecnt++;
    }
    
    bool match(int i){
        S[i] = 1;
        for(int e = head[i]; e != -1; e = nxt[e]) if(!T[v[e]]){
            T[v[e]] = 1;
            if(!fa[v[e]] || match(fa[v[e]])){
                fa[v[e]] = i;
                return 1;
            }
        }
        return 0;
    }
    
    void solve(){
        memset(fa, 0, sizeof(fa));
        int ret = 0;
        for(int i = 1; i <= R; i++){
            memset(S, 0, sizeof(S));
            memset(T, 0, sizeof(T));
            if(match(i)) ret++;
        }
        memset(mo, 0, sizeof(mo));
        memset(S, 0, sizeof(S));
        memset(T, 0, sizeof(T));
        for(int i = 1; i <= C; i++) mo[fa[i]] = 1;
        for(int i = 1; i <= R; i++) if(!mo[i]) match(i);
        printf("%d", ret);
        for(int i = 1; i <= R; i++) if(!S[i]) printf(" r%d", i);
        for(int i = 1; i <= C; i++) if(T[i]) printf(" c%d", i);
        puts("");
    }
    
    int main()
    {
        int N, x, y;
        while(scanf("%d%d%d", &R, &C, &N) == 3){
            if(!R && !C && !N) return 0;
            init();
            for(int i = 1; i <= N; i++){
                scanf("%d%d", &x, &y);
                addEdge(x, y);
            }
            solve();
        }
        return 0;
    }



  • 相关阅读:
    JDK的安装及环境变量部署
    计算机常用运行指令
    Linux基础2
    Linux基础1
    Oracle数据库基础(2)
    Oracle数据库的基础(1)
    测试用例的设计
    软件测试基础
    转化课-环境变量
    转化课-计算机基础及上网过程
  • 原文地址:https://www.cnblogs.com/riskyer/p/3306181.html
Copyright © 2011-2022 走看看