http://poj.org/problem?id=3041
题目意思很明确,求最少武器来消除所有的小行星。初看问题,就是求解最小点覆盖数,也即最大匹配数,但是直接建图肯定会超时,由于匈牙利算法的时间复杂度为O(n^3),n为10000的时候,肯定会超时,这里就以行与列作为点,有小行星的地方,则连一条线。这样就转化为求解最小点覆盖数。二分图的每一条边作为一个小行星,我们找最少的点,使得这些点覆盖图中的所有边,即最小点覆盖,也即最大边匹配数。由于一颗子弹,每次只能使一行或者一列而非同时使得所在的行与列消失,开始的时候没彻底理解这一点,想转换的时候没明白,纠结了一阵。
代码如下:
/*
ID:csuchenan
PROG: POJ 3041
LANG: C++
Algorithm : 匈牙利算法
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std ;
const int maxn = 505 ;
int map[maxn][maxn] ;
int gra[maxn][maxn] ;
int xs[maxn][maxn] ;
int ys[maxn][maxn] ;
int cx[maxn] ;
int cy[maxn] ;
int mk[maxn] ;
int dfs(int u) ;
int maxmatch() ;
int n ;
int m ;
int xn ;
int yn ;
int main()
{
int i ;
int j ;
int p ;
int q ;
cin>>n>>m ;
memset(map , 0 , sizeof(map)) ;
memset(gra , 0 , sizeof(gra)) ;
memset(xs , 0 , sizeof(xs)) ;
memset(ys , 0 , sizeof(ys)) ;
i = 0 ;
while(i < m)
{
cin>>p>>q ;
map[p][q] = 1 ;
i ++ ;
}
int number ;
bool flag ;
flag = 0 ;
number = 1 ;
for(i = 1 ; i <= n ; i ++)
{
flag = 0 ;
for(j = 1 ; j <= n ; j ++)
{
if(map[i][j])
{
xs[i][j] = number ;
flag = 1 ;
}
}
if(flag)
number ++ ;
}
xn = number - 1 ;
number = 1 ;
flag = 0 ;
for(i = 1 ; i <= n ; i ++)
{
flag = 0 ;
for(j = 1 ; j <= n ; j ++)
{
if(map[j][i])
{
ys[j][i] = number ;
flag = 1 ;
}
}
if(flag)
number ++ ;
}
yn = number - 1 ;
for(i = 1 ; i <= n ; i ++)
{
for(j = 1 ; j <= n ; j ++)
{
if(xs[i][j] && ys[i][j])
gra[xs[i][j]][ys[i][j]] = 1 ;
}
}
maxmatch() ;
return 0 ;
}
int maxmatch()
{
int i ;
int ans ;
ans = 0 ;
memset(cx , -1 , sizeof(cx)) ;
memset(cy , -1 , sizeof(cy)) ;
for(i = 1 ; i <= xn ; i ++)
{
if(cx[i] == -1)
{
memset(mk , 0 , sizeof(mk)) ;
ans += dfs(i) ;
}
}
cout<<ans<<endl ;
return 0 ;
}
int dfs(int u)
{
int v ;
for(v = 1 ; v <= yn ; v ++)
{
if(!mk[v] && gra[u][v])
{
mk[v] = 1 ;
if(cy[v] == -1 || dfs(cy[v]))
{
cx[u] = v ;
cy[v] = u ;
return 1 ;
}
}
}
return 0 ;
}