Uva - 11419 - SAM I AM

题意:一个矩形——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;

}


 


 

你可能感兴趣的:(uva)