大意:
有n个点,告诉你一些单向边,问多少条边能把所有的点覆盖【注意点能重复覆盖 比如4->1->2 5->3】
分析:
知识储备:
传递闭包: 所谓传递,可以这么理解,对于节点j如果i能到k并且k能到j那么i能到j,这样用像floyed就能处理出任意两个点能否到达
for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) { if(W[i][k]) { for(int j = 1; j <= n; j++) { W[i][j] = W[i][j] || (W[i][k] && W[k][j]); } } } }
由于点可以被重复利用所以用传递闭包处理处任意两点的到达关系
在求最小路径覆盖就可以了。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 using namespace std; 6 7 const int maxn = 505; 8 const int INF = 1000000000; 9 10 int n; 11 int vis[maxn]; 12 int Link[maxn]; 13 vector<int> G[maxn]; 14 bool Find(int u) { 15 for(int i = 0; i < G[u].size(); i++) { 16 int v = G[u][i]; 17 if(!vis[v]) { 18 vis[v] = 1; 19 if(Link[v] == -1 || Find(Link[v])) { 20 Link[v] = u; 21 return true; 22 } 23 } 24 } 25 return false; 26 } 27 28 int solve() { 29 memset(Link, -1, sizeof(Link)); 30 int cnt = 0; 31 for(int i = 1; i <= n; i++) { 32 if(G[i].size()) { 33 memset(vis, 0, sizeof(vis)); 34 if(Find(i)) cnt++; 35 } 36 } 37 return cnt; 38 } 39 40 int W[maxn][maxn]; 41 int main() { 42 int m; 43 int a, b; 44 while(scanf("%d %d",&n, &m) && ( n + m) ) { 45 for(int i = 1; i <= n; i++) G[i].clear(); 46 memset(W, 0, sizeof(W)); 47 while(m--) { 48 scanf("%d %d",&a, &b); 49 W[a][b] = 1; 50 } 51 for(int k = 1; k <= n; k++) { 52 for(int i = 1; i <= n; i++) { 53 if(W[i][k]) { 54 for(int j = 1; j <= n; j++) { 55 W[i][j] = W[i][j] || (W[i][k] && W[k][j]); 56 } 57 } 58 } 59 } 60 for(int i = 1; i <= n; i++) { 61 for(int j = 1; j <= n; j++) { 62 if(W[i][j]) G[i].push_back(j); 63 } 64 } 65 printf("%d\n",n - solve()); 66 } 67 return 0; 68 }
POJ 2594 Treasure Exploration【传递闭包+最小路径覆盖】
原文:http://www.cnblogs.com/zhanzhao/p/3940434.html