这道题实现起来还是比较简单的,但是理解起来可能有点困难。

我最开始想到的是贪心法,每次消灭当前小行星最多的一行或一列。然而WA了。Discuss区里已经有高人给出反例。

下面给出正确的解法

我们把行和列抽象成点,把小行星抽象成边,每出现一个小行星,就把其行列所对应的点连起来。这样就形成了一个无向图$G=\left(V, E\right)$。问题就转化为了求这个图G中的最小点覆盖,即求一个元素数量尽可能小的点集$V' \subset V$,$E$中的所有边均与其内的一点相连。

最小点覆盖问题是一个NP困难问题,目前没有已知的能够在多项式时间内解决该问题的算法。

不过我们可以这样考虑:

我们给出一个定义:设存在一个集合${V}_{1} \subset V, ~ {V}_{2} = V - {V}_{1}$。若$a \in {V}_{1}, ~ b \in {V}_{2}, ~ (a, b) \in E$,那么我们称这条边$(a, b)$为一个匹配。

若有两个点集${V}_{1} \subset V, ~ {V}_{2} = V - {V}_{1}$,两者并不相交。若有一种方案,使得${V}_{1}$和${V}_{2}$的所有匹配之间均没有重复的点,且${V}_{1}$内的所有点均拥有至少一个匹配。则当${V}_{1}$的元素数量最大时,${V}_{1}$就是这张图的最小点覆盖。

我们这里做一个简单的不严谨的证明,便于理解:

  1. ${V}_{1}$的元素数量没有达到最大,意味着至少有一条边,它的两个端点都不在${V}_{1}$中。显然${V}_{1}$不是这张图的一个点覆盖,因为它不符合点覆盖的定义。
  2. 假设${V}_{1}$是这张图的一个点覆盖。如果${V}_{1}$中存在一个点$v$,它在${V}_{2}$中没有满足上述条件的对应点。那么要么没有边与该点相连,要么与其相连的边的另一个端点全在${V}_{1}$内。不论如何,我们都可以把$v$从${V}_{1}$中去掉,其仍然是这张图的一个点覆盖。换句话说,此时的${V}_{1}$并不是最小点覆盖。
  3. 假设${V}_{1}$是这张图的一个点覆盖。${V}_{2} = V - {V}_{1}$。

综上所述,若存在某一条边,其两个点均不在${V}_{1}$中,那么${V}_{1}$就不是这张图的点覆盖。若${V}_{1}$是此图的一个点覆盖,其中存在某个点,它在${V}_{2} = V - {V}_{1}$中没有边与其相连,那么${V}_{1}$不是该图的最小点覆盖。若没有一种方案,使得所有的匹配之间都没有重复的点,那么${V}_{1}$也不是该图的最小点覆盖。

图中的所有点要么在${V}_{1}$中,要么在${V}_{2} = V - {V}_{1}$中。我们每找到一条两点$(a, b)$均不在${V}_{1}$中的边,就将$a$从${V}_{2}$中移入${V}_{1}$,将这条边记作一个匹配。当然,如果有必要,我们也可以将$a$移回${v}_{2}$,再将$b$移进${V}_{1}$;或者将$a$和$b$都移回${V}_{2}$,删除这条匹配。若我们所找到的匹配没有达到最大数量,则意味着还存在有至少一条边,它的两个点均不在${V}_{1}$中。此时的${V}_{1}$并不是这张图的点覆盖。

并且,根据证明3,我们这里所找到的匹配,它们中任意两条之间不能拥有重复的点。

而当我们找到的符合条件的边(也就是匹配)的数量最大时,${V}_{1}$刚好变成了这张图的一个点覆盖,并且,它时这张图的最小点覆盖。

这时,找点的问题被转化成了找边的问题。

在这个具体的问题里,我们所找到的符合条件的边,其两端点必定一个代表行,一个代表列。也就是说,如果我们将$V$划分为代表行的点集${V}_{r}$和代表列的点集${V}_{c}$,则所有的匹配一定是跨越了这两个集合的。

这样,所有的边$(a, b)$不仅是${V}_{1}$、${V}_{2}$之间的匹配,也是${V}_{r}$和${V}_{c}$间的匹配。并且,我们这里所找到的所有的边,它们的点都不重复(否则就违反了刚才的证明3)。因此,找边的问题就变成了找二分匹配的问题。找到的边最大就变成了找到的二分匹配最大。

这个问题就被转化成了一个最大二分匹配问题。

最大二分匹配问题,可以用网络流的手段解决,也可以用匈牙利算法解决。匈牙利算法是一种专门用来解决二分匹配问题的算法,在这里就不具体解释了。不过这道题也是我第一次用这个算法解决问题,错了很多次。

 #include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream> int n, k; bool dfs_visited[];
int graph[][];
int matches[]; bool Hungarian_Aux(int vertex) { for(int i = ; i <= n; ++i) {
if(!graph[vertex][i] || dfs_visited[i])
continue;
dfs_visited[i] = true;
if(matches[i] == || Hungarian_Aux(matches[i])) {
matches[i] = vertex;
return true;
}
} return false;
} int Hungarian_Algorithm() {
int matches_count = ;
for(int vertex = ; vertex <= n; ++vertex) {
memset(dfs_visited + , , n * sizeof(bool));
if(Hungarian_Aux(vertex)) {
++matches_count;
}
} return matches_count;
} int main() {
scanf("%d%d", &n, &k);
int r, c;
for(int i = ; i <= k; ++i) {
scanf("%d%d", &r, &c);
graph[r][c] = true;
}
printf("%d\n", Hungarian_Algorithm()); return ;
}

POJ 3041 - 最大二分匹配的更多相关文章

  1. poj 2446 Chessboard (二分匹配)

    Chessboard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 12800   Accepted: 4000 Descr ...

  2. POJ 3057 Evacuation (二分匹配)

    题意:给定一个图,然后有几个门,每个人要出去,但是每个门每个秒只能出去一个,然后问你最少时间才能全部出去. 析:初一看,应该是像搜索,但是怎么保证每个人出去的时候都不冲突呢,毕竟每个门每次只能出一个人 ...

  3. POJ 2289 多重二分匹配+二分 模板

    题意:在通讯录中有N个人,每个人能可能属于多个group,现要将这些人分组m组,设各组中的最大人数为max,求出该最小的最大值 下面用的是朴素的查找,核心代码find_path复杂度是VE的,不过据说 ...

  4. POJ 3041 Asteroids 二分图匹配

    以行列为点建图,每个点(x,y) 对应一条边连接x,y.二分图的最小点覆盖=最大匹配 //#pragma comment(linker, "/STACK:1024000000,1024000 ...

  5. POJ 1469 ZOJ1140 二分匹配裸题

    很裸,左点阵n,右点阵m 问最大匹配是否为n #include <cstdio> #include <cstring> #include <vector> usin ...

  6. poj 1469 COURSES (二分匹配)

    COURSES Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16877   Accepted: 6627 Descript ...

  7. Guardian of Decency POJ - 2771 【二分匹配,最大独立集】

    Problem DescriptionFrank N. Stein is a very conservative high-school teacher. He wants to take some ...

  8. POJ 1274 The Perfect Stall、HDU 2063 过山车(最大流做二分匹配)

    The Perfect Stall Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 24081   Accepted: 106 ...

  9. poj 1247 The Perfect Stall 裸的二分匹配,但可以用最大流来水一下

    The Perfect Stall Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16396   Accepted: 750 ...

随机推荐

  1. python编程——断言

    基本语法 assert_stmt ::= "assert" expression ["," expression] assert 5 > 3 # 肯定是对 ...

  2. noip模拟赛 站军姿

    分析:纯数学题.相离和包含关系的可以很容易算出来答案,相交的话要先求出两个圆的面积,然后减掉中间重叠的部分,这一部分并不能直接求出来,但是可以求出两个扇形的面积,和它们围成的一个四边形的面积,加加减减 ...

  3. [hdu2222] [AC自动机模板] Keywords Search [AC自动机]

    AC自动机模板,注意!ch,Fail,lab数组的大小不是n而是节点个数,需要认真计算! #include <iostream> #include <algorithm> #i ...

  4. Method and apparatus for speculative execution of uncontended lock instructions

    A method and apparatus for executing lock instructions speculatively in an out-of-order processor ar ...

  5. Android 聊天表情输入、表情翻页带效果、下拉刷新聊天记录

    经过一个星期的折腾,最终做完了这个Android 聊天表情输入.表情翻页带效果.下拉刷新聊天记录.这仅仅是一个单独聊天表情的输入,以及聊天的效果实现.由于我没有写server,所以没有两方聊天的效果. ...

  6. js面向对象初步探究(上) js面向对象的5种写方法

    非常长一段时间看网上大神的JS代码特别吃力.那种面向对象的写法方式让人看得云里来雾里去.于是就研究了一下JS面向对象.因为是初学,就将自己在网上找到的资料整理一下,作为记忆. js面向对象的5种写方法 ...

  7. Axure RP一个专业的高速原型设计工具

    Axure RP是一个专业的高速原型设计工具.Axure(发音:Ack-sure),代表美国Axure公司.RP则是Rapid Prototyping(高速原型)的缩写. Axure简要介绍 Axur ...

  8. 替换文件里的相关单词(一)之文件类型为txt

    首先说一下详细的实现思路: 第一步:我们须要获取要改动文件的信息,我们能够通过文件的路径来获取文件的FileInputStream,即文件的输入流,然后调用InputStreamReader读取文件输 ...

  9. C++ 句柄类的原理以及设计

    句柄类存在的意义是为了弥补将派生类对象赋给基类对象时发生的切片效应.比如以下的程序: multimap<Base> basket; Base base; Derived derive; b ...

  10. 浅析android适配器adapter中的那些坑

    做项目中遇到的,折磨了我将近两天,今天把经验分享出来.让大家以后少走点弯路,好了.简单来说一下什么是android的适配器,怎样定义.怎样添加适配器的重用性.怎样去减少程序的耦合性 适配器顾名思义是用 ...