poj2226(最小顶点覆盖)

224 阅读2分钟

(相当于是poj3041的进阶版,不过难度还好)
题目大概意思为将一个矩阵中的泥泞部分给覆盖起来,可以横着覆盖也可以竖着覆盖,但不能覆盖到其他草地部分,覆盖的板子长度随意,宽度为1,可以重复覆盖,求最少需要的板子数量

解题思路:把板子当成图的顶点,把泥泞的点当作连接对应板子(一个横着的,一个竖着的)的边,这样转化之后,求最少需要的板子数量,即相当于我们对于任意边,都至少需要一个该边的端点,即转为成了求最小顶点覆盖问题。
由于所有的泥泞的点,连接的都是横着的和竖着的,所以这是二分图,在二分图中,|最小顶点覆盖| = |最大匹配|,问题便迎刃而解

需要考虑的地方即如何把板子转化成顶点:
看成总共有 N * M * 2 个板子,每 i 行 j 列为顶点都有横着摆和竖着摆两种
假如有N行,M列,我们把 i * M + j 看成从 i 行 j 列开始横着摆放的板子,把 N * M + j * N + i 看成从 j 列 i 行开始竖着摆放的板子
例:

4 4
*.*.
.***
***.
..*.

其中第1行第2列(从0开始)就相当于可以从第1行第1列开始横着摆,或从第2列第0行开始竖着摆,所以把 1 * M + 1 和 N * M + 2 * N + 0 连接在一起

其他细节我就注释在代码中

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
int data[55][55];
int flag_x[55];   //用于记录当前第i行左边最远连续的泥泞点的位置
int flag_y[55];	  //用于记录当前第j列上边最远连续的泥泞点的位置
bool used[2505];
int match[2505];
vector<int> edge[5005];
void add_edge(int from, int to)
{
    edge[from].push_back(to);
    edge[to].push_back(from);
}
bool dfs(int v)
{
    used[v] = true;
    for(int i = 0; i < edge[v].size(); i++)
    {
        int u = edge[v][i], w = match[u];
        if(w == -1 || !used[w] && dfs(w))
        {
            match[u] = v;
            match[v] = u;
            return true;
        }
    }
    return false;
}
int main()
{
    int N, M;
    char ch;
    memset(flag_x, -1, sizeof(flag_x));
    memset(flag_y, -1, sizeof(flag_y));
    scanf("%d %d", &N, &M);
    for(int i = 0; i < N; i++)
    {
        getchar();
        for(int j = 0; j < M; j++)
        {
            scanf("%c", &ch);
            if(ch == '*')
            {
                data[i][j] = 1;
            }
            else
            {
                data[i][j] = 0;
            }
        }
    }
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < M; j++)
        {
            if(data[i][j] == 1)
            {
                if(flag_x[i] == -1)
                {
                    flag_x[i] = j;
                }
                if(flag_y[j] == -1)
                {
                    flag_y[j] = i;
                }
                add_edge(i * M + flag_x[i], N * M + j * N + flag_y[j]);
            }
            else
            {
                flag_x[i] = -1;
                flag_y[j] = -1;
            }
        }
    }
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < M; j++)
        {
            memset(used, false, sizeof(used));
            if(dfs(i * M + j))
            {
                res++;
            }
        }
    }
    printf("%d\n", res);
}