http://poj.org/problem?id=3041
题意: n×n的网络中有k个怪兽(i,j),你的一次攻击可以消灭一整行或者一整列的怪兽,问你最少需要进行多少次攻击才能把怪兽消灭完毕。(改了一下题面描述2333)
一开始我又双想到的是贪心,策略就是一次尽可能消灭多的怪兽,结果在我意料当中,这种贪心显然行不通。(每次我觉得可以贪心就不能用贪心做orz。。)
1 1 0 0
1 0 1 0
1 0 0 1 (1表示怪兽0表示没有,你给我贪啊!贪!。。。。)
其实我一直都没意识到是二分图,可能是我二分图除了模板题就没做过其他题的原因(其实是菜。。)
这道题假如以怪兽建图的话比较难建,题解是以攻击建图。
因为目的是消灭完所有的怪兽,所以每只怪兽都必定会受到攻击,攻击的来源只有两种,横向攻击和纵向攻击,因此我们可以以攻击的横纵建立二分图。图的一边以: 第一行横线攻击,第二行横向攻击…… 来建立顶点,另外一边则用纵向攻击为顶点。以怪兽会受到的攻击为边,比如 (1,2)的怪兽可以被 第一行行攻击以及第二列列攻击杀死,因此就有一条边链接(1,2)。那么如此要消灭完所有怪兽,就是所有的边都至少链接一个顶点。
对于同一个顶点而言,连接它的边无论有多少条,我们都只需要选择其中一条边就可以代表它了,因此我们只需要找出这幅图的最大二分匹配就可以了。
对于二分匹配推荐一个博客: https://blog.youkuaiyun.com/dark_scope/article/details/8880547
代码:(使用匈牙利算法)
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn=10005;
int n,m;
vector<int> V[maxn];
int used[maxn],match[maxn];
bool bfs(int num){
used[num]=1;
for(int i=0;i<V[num].size();i++){
int to=V[num][i];
if((!match[to])||(!used[match[to]]&&bfs(match[to]))){
match[to]=num;
return 1;
}
}
return 0;
}
int two_part_matching(){ //二分匹配模板哟
memset(match,0,sizeof(match));
memset(used,0,sizeof(used));
int res=0;
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
if(bfs(i)){
res++;
}
}
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int num1,num2;
scanf("%d %d",&num1,&num2);
V[num1].push_back(num2);
}
cout<<two_part_matching()<<endl;
return 0;
}