POJ-3020 Antenna Placement 二分图匹配+拆点构图

  这题和其他的要用二分图匹配不太一样,只要是它允许有重叠的点可以利用,而寻找增广路径的方式是不允许一点重复利用的。所以这题要转换一下方式来间接求得。

  首先对于所有的孤立进行搜索,顺带建立边的关系,对于孤立的节点一定要安放一个雷达的。然后对于剩下的的,根据贪心思想,让一个天线直接覆盖两个点的话是最优的策略,于是我们利用二分图匹配这个方法使得所有的匹配成功的边都安装天线。接下来我们再将所有没有覆盖到的点在适当位置安放天线(势必会有重复覆盖的点)。最后将三者的值相加即可。当然第一个结果和第三个结果是可以一起算的。

  代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

char G[45][15];

int H, W, cnt, dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
int visit[45][15], Map[45][15][45][15], match[45][15][2];

bool isalone(int x, int y)
{
    int xx, yy;
    bool flag = true;
    for (int i = 0; i < 4; ++i) {
        xx = x+dir[i][0], yy = y+dir[i][1];
        if (x >= 1 && x <= H && y >= 1 && y <= W && G[xx][yy] == '*') {
            flag = false;  
            Map[x][y][xx][yy] = 1;
        }
    }
    return flag;
}

bool path(int x, int y)
{
    for (int i = 1; i <= H; ++i) {
        for (int j = 1; j <= W; ++j) {
            if (Map[x][y][i][j] && !visit[i][j]) {
                visit[i][j] = 1;
                if (!match[i][j][0] || path(match[i][j][0], match[i][j][1])) {
                    match[i][j][0] = x, match[i][j][1] = y;
                    return true;
                }
            } 
        }
    }
    return false;
}

int main()
{
    int T, ans;
    scanf("%d", &T);
    while (T--) {
        cnt = ans = 0;
        memset(match, 0, sizeof (match));
        memset(Map, 0, sizeof (Map));
        scanf("%d %d", &H, &W);
        for (int i = 1; i <= H; ++i) {
            scanf("%s", G[i]+1);
        }
        for (int i = 1; i <= H; ++i) {
            for (int j = 1; j <= W; ++j) { 
                if (G[i][j] == '*' && isalone(i, j)) {
                    ++cnt;
                    G[i][j] = 'o';
                }
            }
        }
        for (int i = 1; i <= H; ++i) {
            for (int j = 1; j <= W; ++j) {
                memset(visit, 0, sizeof (visit));
                if (path(i, j)) {
                    ++ans;
                }
            }
        }
        
        for (int i = 1; i <= H; ++i) {
            for (int j = 1; j <= W; ++j) {
                if (G[i][j] == '*' && !match[i][j][0]) {
                    ++cnt;
                }
            }
        }
        printf("%d\n", cnt + (ans>>1));
    }
    return 0;
}

 

关于为什么最后匹配数要除以2我是这样认为的,二分图匹配算法本来是作用与一个二分图上面的,现如今要运用到一个二维的图上,我们只有将二维图复制出一个出来,这个在Map函数上也可以看出来,复制出来以后,我们就需要构边了,由于我们所有的边都是重复的,所以最后计算出来的结果就是真是匹配数的两倍了。你可能会说,能不能建立单向的边,来使得所有的计算次数减少,并且不用去除以那个2,其实这是不可行的,因为严格的二分图是图的某一部分内部是没有边的存在,所有的边都是从这一步分指向另外一部分。这里没有办法划分出哪些点只出边不入边。

自己也糊涂了。

 

再次理解:

其实该题求了一个最大匹配的结果(就是一个贪心的思想),即求一个最小的点集使得所有的边的至少有一个端点都落在这个点集之内。由于该题采用拆点的方式来进行构图,之所以可以拆点有一点非常重要,那就是从一个点引出的边之间是不存在边的,这样保证了该点不会与相邻点出现两条以上的边。因此最终的匹配数会乘上2倍,其实该题也可以采用奇偶染色来构图,最后的结果就不需要除以2了。

posted @ 2012-07-02 21:58  沐阳  阅读(696)  评论(0编辑  收藏  举报