【最小顶点覆盖数】POJ 3041 + POJ 2226+HDU 5093

很多问题都可以转化为二分图匹配模型。二分图有如下几种常见变形:
(1)二分图的最小顶点覆盖 
最小顶点覆盖要求用最少的点(X或Y中都行),让每条边都至少和其中一个点关联。
Knoig定理:二分图的最小顶点覆盖数等于二分图的最大匹配数。
(2)DAG图的最小路径覆盖 
用尽量少的不相交简单路径覆盖有向无环图(DAG)G的所有顶点,这就是DAG图的最小路径覆盖问题。
结论:DAG图的最小路径覆盖数 = 节点数(n)- 最大匹配数(m)
(3)二分图的最大独立集
最大独立集问题: 在N个点的图G中选出m个点,使这m个点两两之间没有边.求m最大值
结论:二分图的最大独立集数 = 节点数(n)— 最大匹配数(m)

参见博客:http://blog.csdn.net/acdreamers/article/details/8621130

1、题目链接:http://poj.org/problem?id=3041

题意:假如你现在处于一个N*N的矩阵中,这个矩阵里面有K个障碍物,你拥有一把武器,一发弹药一次能消灭一行或者一列的障碍物,求消灭全部的障碍物需要最少的弹药数。
分析:本问题是二分图的一个很经典的模型,我们可以这样考虑:我们以所有的行为二分图的左顶点,所有的列为二分图的右顶点,那么如果位于坐标P(x,y)有障碍物,我们就连一条边,然后我们只需要最少的顶点覆盖所有的边即可。这样就是二分图的最小顶点覆盖问题了,我们又知道最小顶点覆盖等于二分图最大匹配。

二分图最大匹配的模板题,代码就不贴了。

2、题目链接:http://poj.org/problem?id=2226

题意:给出一个N*M的图,其中图中每一个格子要么是'*',要么是'.','*'代表稀泥,'.'代表草地,现在要用一些木板把所有的稀泥盖住,但是不能盖住草地。一张木板只能盖住一行或者一列中的一部分,求至少要用多少木板把所有的稀泥盖住。

分析:木板不能盖住整行或者整列,但是我们可以把所有的横向连续的稀泥格子和所有竖向连续的稀泥格子编上号,然后如果有交叉的就连一条边,这就是建图部分。然后进一步转化为最小顶点覆盖问题。

本题的难点是转化为图,建立联系

4 4      横向编号:        竖向编号:
*.*.       1 0 2 0        1 0 4 0
.***       0 3 3 3        0 3 4 5
***.       4 4 4 0        2 3 4 0
..*.       0 0 5 0        0 0 4 0
通过编号我们发现就是对编号一一对应建立边,然后找最小的顶点覆盖数覆盖尽可能多的边,主要是思路问题,还有对二分匹配的深入了解,具体代码如下:

Code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mann 1001
#define INF 0x3f3f3f3f
int link[mann],vis[mann];
int head[mann];
int n,m,aa,bb,cnt=0;
char s[mann][mann];
int a[mann][mann];
int b[mann][mann];
struct edge
{
    int to,next;
} eg[mann*100];
void add_edge(int u,int v)
{
    eg[cnt].to=v;
    eg[cnt].next=head[u];
    head[u]=cnt++;
}
bool dfs(int u)
{
    for(int i=head[u]; i!=-1; i=eg[i].next)
    {
        int v=eg[i].to;
        if(!vis[v])
        {
            vis[v]=1;
            if(link[v]==-1||dfs(link[v]))
            {
                link[v]=u;
                return true;
            }
        }
    }
    return false;
}
int match()
{
    int ans=0;
    memset(link,-1,sizeof(link));
    for(int i=1; i<=aa; i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i))
            ans++;
    }
    return ans;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i=0)
                        a[i][j]=a[i][j-1];
                    else
                        a[i][j]=++aa;
                }
            }
        }
        for(int j=0; j=0)
                        b[i][j]=b[i-1][j];
                    else
                        b[i][j]=++bb;
                    add_edge(a[i][j],b[i][j]+aa);//建立边之间的联系
                    add_edge(b[i][j]+aa,a[i][j]);
                }
            }
        }
//        for(int i=0; i

3、题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5093

题目大意:给出一个n行m列的图,*代表海域,o代表冰水,#代表冰山,要想在海域中放置船,保证船与船之间不能相互看到,之间只要有山就不能看到,问最多能放多少船。

本题和上一题不同的是,中间多出了一个状态,重点是如何建图达到最大匹配状态。

思路:用初始变量标记建图,如果中间出现冰山,变量自加一,不连续的自然数不影响最终结果(自己思考),然后遇到海域就进行编号,最后建成的图,一个为左顶点,另一个为右顶点进行建二分图,求出最大匹配即可。

Code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
#define mann 2001
#define INF 0x3f3f3f3f
char s[mann][mann];
int a[mann][mann],b[mann][mann];
int head[mann],vis[mann];
int link[mann];
int mp[mann][mann];
int n,m,cnt=0;
int aa,bb;
bool dfs(int u)
{
    for(int i=1; i<=bb; i++)
    {
        if(mp[u][i]&&!vis[i])
        {
            vis[i]=1;
            if(link[i]==-1||dfs(link[i]))
            {
                link[i]=u;
                return true;
            }
        }
    }
    return false;
}
int match()
{
    int ans=0;
    memset(link,-1,sizeof(link));
    for(int i=1; i<=aa; i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i))
            ans++;
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(mp,0,sizeof(mp));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(a));
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=0; i


这种类型的题主要是对题意得理解,然后转化为对应的模型,还是要有深入的理解。

你可能感兴趣的:(搜索)