题意:在方格图上打小怪,每次可以清除一整行或一整列的小怪,问最少的步数是多少,又应该在哪些位置操作(对输出顺序没有要求)。

分析:最小覆盖问题

  这是一种在方格图上建立的模型:令S集表示“行”,T集表示“列”,那么小怪站的位置w(i,j),就是二分图上的边。如此建图,那么每次清除,就是把与某个点相连的边全部清除,问最少选择多少个点。(这也是最小点覆盖的概念:选择尽量少的点,使得每条边至少有一个端点被选中)

  这里有一个König定理:最大二分匹配数==最小覆盖点数。

  既然是求最小点覆盖,那么自然是选那些所连边数多的点,不过貌似不好安排啊?

  先从简单问题开始讨论:找到必然要选的点。对于一棵树,为了覆盖到叶子节点所在的边,必须选择该条边上的两个端点之一。不用问,按照最小覆盖的原则,必然是要选非叶子节点了。把所有与选择的点相连的边都处理掉,就相当于是这棵树的最下面一层被砍掉了(叶子的爷爷成为了新的叶子)。那么对于剩下的树,仍然可以这样操作,最终得到了最小点覆盖。幸运的是,树属于二分图,而二分图虽然不一定是树(存在偶环),但可以构建出匈牙利树(交错树),是可以借鉴的。

  聪明的地球人提出了如下算法:

  对于一个二分图,求出其最大匹配。然后取左侧(S侧)所有的未匹配点,按照增广路算法,寻找交错路径(路径上的点都是匹配点),标记掉路径上的所有点(不存在重复标记)。那么左侧(S侧)所有的未标记点,与右侧(T侧)所有的标记点,就是实现最小点覆盖的点。

  1、为什么这些点可以覆盖所有边?

  如果还有没有覆盖的边,那么一定是一些左端点已标记,右端点未标记的边。而通过我们算法中的构造,不存在这样的边。为什么呢?回忆一下增广路算法,我们的起点选择的是S侧的未匹配点,之后选择一条边走到T侧(该点必然是匹配点);因为要走交错路径,就沿着匹配边回到了S侧...可以想象成每次只是从S侧走到T侧,然后沿匹配边回到S侧。所以不存在S端已标记,T端未标记的线段。所以所有边都被覆盖了。

  2、为什么这些点的在数量上等于最大二分匹配的边数?

  因为每条匹配边上恰好选择一个点。为什么是这样呢?注意,最小点覆盖的点集包括:S侧的未标记点,和T侧的标记点。有增广路算法可知(同上一个问题相似),若T侧的点被标记,那么同一条匹配边上的S侧的点必然也被标记——点集中不包含同一条匹配边上的两个点。S侧的未标记点,实际上是除去未匹配点,以及跟随T侧点而被标记的点后,剩余的(即选择了那些T侧未被标记的匹配边)。所以两侧的点把所有的匹配边都包含了。所以最大二分匹配数==最小覆盖点数。

  3、为什么这些点就是最小值?

  这个问题最简单:及使图上只有n条匹配边,我们都要用n个点才能覆盖。若再少,至少漏掉一条匹配边。所以是最小值。

(注:难得自己动手改了张图...圆圈是覆盖点,方格是标记点,箭头是交错路径,蓝线是匹配边。)

可以看看这个链接,我认为上面的解释更通俗一些。

http://www.matrix67.com/blog/?s=%E6%9C%80%E5%B0%8F%E8%A6%86%E7%9B%96%E7%82%B9

 #include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define clr(a,m) memset(a,m,sizeof(a))
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std; const int MAXN=; int n,m,cnt,ans; vector<int>G[MAXN];
vector<int>row,col; int left[MAXN],right[MAXN],S[MAXN],T[MAXN]; void init()
{
rep(i,,n)
G[i].clear();
} void read()
{
int x,y;
init();
rep(i,,cnt){
scanf("%d%d",&x,&y);
G[x].push_back(y);
}
} bool match(int u)
{
S[u]=true;
int sz=G[u].size();
rep(i,,sz-){
int v=G[u][i];
if(!T[v]){
T[v]=true;
if(!left[v]||match(left[v])){
left[v]=u;
right[u]=v;
return true;
}
}
}
return false;
} void AP()
{
rep(i,,m)left[i]=;//n和m写反了
rep(i,,n)right[i]=; ans=;
rep(i,,n){
rep(j,,n)S[j]=;
rep(j,,m)T[j]=;
if(match(i))
ans++;
}
} void mincover()
{
rep(i,,n)S[i]=;//重新标记增广路
rep(i,,m)T[i]=; rep(i,,n)//选择S侧的未标记点
if(!right[i])
match(i); row.clear();
col.clear();
rep(i,,n)
if(!S[i])
row.push_back(i);
rep(i,,m)
if(T[i])
col.push_back(i);
} void print()
{
printf("%d",ans);
int sz=col.size();
rep(i,,sz-)
printf(" c%d",col[i]);
sz=row.size();
rep(i,,sz-)
printf(" r%d",row[i]); printf("\n");
} int main()
{
while(~scanf("%d%d%d",&n,&m,&cnt))
{
if(!n&&!m&&!cnt)
return ;
read();
AP();
mincover();
print();
}
return ;
}

UVA 11419 SAM I AM(最大二分匹配&最小点覆盖:König定理)的更多相关文章

  1. nyoj 237 游戏高手的烦恼 二分匹配--最小点覆盖

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=237 二分匹配--最小点覆盖模板题 Tips:用邻接矩阵超时,用数组模拟邻接表WA,暂时只 ...

  2. HDU - 1150 Machine Schedule(二分匹配---最小点覆盖)

    题意:有两台机器A和B,A有n种工作模式(0~n-1),B有m种工作模式(0~m-1),两台机器的初始状态都是在工作模式0处.现在有k(0~k-1)个工作,(i,x,y)表示编号为i的工作可以通过机器 ...

  3. Uva - 11419 - SAM I AM

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

  4. UVA - 12083 Guardian of Decency (二分匹配)

    题意:有N个人,已知身高.性别.音乐.运动.要求选出尽可能多的人,使这些人两两之间至少满足下列四个条件之一. 1.身高差>40  2.性别相同  3.音乐不同  4.运动相同 分析: 1.很显然 ...

  5. UVA 11419 SAM I AM (最小点覆盖,匈牙利算法)

    题意:给一个r*c的矩阵,某些格子中可能有一些怪物,可以在一行或一列防止一枚大炮,大炮会扫光整行/列的怪,问最少需要多少炮?输出炮的位置. 思路: 先每行和列都放一个炮,把炮当成点,把怪当成边,一边连 ...

  6. POJ 3020 Antenna Placement【二分匹配——最小路径覆盖】

    链接: http://poj.org/problem?id=3020 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=22010#probl ...

  7. poj2226-Muddy Fields二分匹配 最小顶点覆盖 好题

    题目 给到一个矩阵,有些格子上是草,有些是水.需要用宽度为1,长度任意的若干块木板覆盖所有的水,并不能覆盖草,木板可以交叉,但只能横竖放置,问最少要多少块板. 分析 经典的矩阵二分图构图和最小点覆盖. ...

  8. UVa 11419 SAM I AM (最小覆盖数)

    题意:给定一个 n * m 的矩阵,有一些格子有目标,每次可以消灭一行或者一列,问你最少要几次才能完成. 析:把 行看成 X,把列看成是 Y,每个目标都连一条线,那么就是一个二分图的最小覆盖数,这个答 ...

  9. UVA 1201 - Taxi Cab Scheme(二分图匹配+最小路径覆盖)

    UVA 1201 - Taxi Cab Scheme 题目链接 题意:给定一些乘客.每一个乘客须要一个出租车,有一个起始时刻,起点,终点,行走路程为曼哈顿距离,每辆出租车必须在乘客一分钟之前到达.问最 ...

随机推荐

  1. light oj 1140 - How Many Zeroes? 数位DP

    思路:dp[i][j]:表示第i位数,j表示是否有0. 代码如下: #include<iostream> #include<stdio.h> #include<algor ...

  2. lintcode:最长公共子序列

    题目 最长公共子序列 给出两个字符串,找到最长公共子序列(LCS),返回LCS的长度. 样例 给出"ABCD" 和 "EDCA",这个LCS是 "A& ...

  3. Hadoop基础教程之高级编程

    从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成<key, value>. 2 ...

  4. MDK5.01百度云下载,安装微软雅黑混合字体,字体效果很棒,解决显示中文的BUG

    微软雅黑字体http://pan.baidu.com/s/1nt9Epuh 初步尝试,以前的小BUG都已经解决了.下面是安装雅黑字体后的字体效果,很爽.第一步:安装雅黑字体.第二步:选择Edit--- ...

  5. ./jad: error while loading shared libraries: libstdc++-libc6.2-2.so.3: cannot open shared object file: No such file or directory

    Ubuntu 上使用jad,出现上面错误: ./jad: error while loading shared libraries: libstdc++-libc6.2-2.so.3: cannot ...

  6. 常见的css3缩放效果

    transform的属性scale(x,y) 对元素进行缩放,x表示水平方向缩放倍数,y表示垂直方向的缩放倍数,y是可选参数,不设置,则表示两个方向的倍数是一样的,基点一样在元素的中心位置. 还有单向 ...

  7. yii2 增加新的目录结构

    搭建新的目录结构详细点击这里 搭建Restful API 点击这里 1.开发环境操作系统        Windows Server 2012 R2 DatacenterPHP架构        Ap ...

  8. POJ3252——Round Number(组合数学)

    Round Numbers DescriptionThe cows, as you know, have no fingers or thumbs and thus are unable to pla ...

  9. DWR3.0 dwr 返回值(数组,集合,Map)

    首先导入项目所需要的包,如下:dwr.jar,commons-logging-1.0.4.jar,版本可以调整 1.web.xml<?xml version="1.0" en ...

  10. c++ 在客户端的GCC使用

    c++ 在客户端的GCC使用 翻译自: GCC是GNU工具箱的一个功能,GNU还包括如下功能: GNU compiler collection (GCC) GNU make GNU Debugger ...