POJ 3041

题意:有一个N*N的格子,里面有-和*。一次命令可以消除某一行或某一列所有的*。请用最少的命令次数,消除所有的*。

 

思路:我之前不知道有Konig定理。

最小点覆盖:在二分图中,选择最少的点,使得每一边都至少有端点被覆盖。

Konig定理:最小点覆盖数=最大匹配数

 

于是,将格子转化成左边N个点,右边N个点的二分图。边XiYj表示i,j有*。这样,只要这条边被选择,所有这一行的*都被消除,因为他们的边都一定有端点Xi.

 

匈牙利算法就可以了

 

统计:440k, 32ms, 1Y

 

#include <iostream> #define F(i,a,b) for (int i = a; i <= b; i++ ) #define maxn 502 using namespace std; bool mk[maxn], map[maxn][maxn]; int match[maxn], n, m; bool dfs(int x) //寻找增广链,true表示找到 { for (int i=1;i<=n;i++) { if ( map[x][i] && !mk[i] ) { mk[i]=true; int t=match[i]; match[i]=x; if (t==0 || dfs(t)) return true; match[i]=t; } } return false; } int hungarian() { int max_=0; F(i,1,n) { memset(mk, false, sizeof(mk)); if ( dfs(i) ) max_++; } return max_; } int main() { int a, b, k; scanf ("%d%d", &n, &k); F(i, 1, k) { scanf ("%d%d", &a, &b); map[ a ][ b ]=true; } cout << hungarian(); return 0; }

 

你可能感兴趣的:(POJ 3041)