题目链接:http://hihocoder.com/problemset/problem/1394
相当于数一数最少有多少条链,这就是最小路径覆盖问题:给定一个有向无环图,用最少的路径数量去保证所有点都被覆盖住。
利用有向图中一条链的前驱和后继唯一(也可能没有)这一条性质就可以建立二分图,最大匹配出的结果就相当于确定了前驱和后继的关系。
记住这个模型!
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 5050; 5 const int inf = 0x3f3f3f3f; 6 int nx,ny; 7 int G[maxn][maxn]; 8 int linker[maxn],lx[maxn],ly[maxn]; 9 int slack[maxn]; 10 bool visx[maxn],visy[maxn]; 11 12 bool dfs(int x) { 13 visx[x] = true; 14 for(int y = 0; y < ny; y++) { 15 if(visy[y])continue; 16 int tmp = lx[x] + ly[y] - G[x][y]; 17 if(tmp == 0) { 18 visy[y] = true; 19 if(linker[y] == -1 || dfs(linker[y])) { 20 linker[y] = x; 21 return true; 22 } 23 } 24 else if(slack[y] > tmp) 25 slack[y] = tmp; 26 } 27 return false; 28 } 29 int km() { 30 memset(linker,-1,sizeof(linker)); 31 memset(ly,0,sizeof(ly)); 32 for(int i = 0;i < nx;i++) { 33 lx[i] = -inf; 34 for(int j = 0;j < ny;j++) 35 if(G[i][j] > lx[i]) 36 lx[i] = G[i][j]; 37 } 38 for(int x = 0;x < nx;x++) { 39 for(int i = 0;i < ny;i++) 40 slack[i] = inf; 41 while(true) { 42 memset(visx,false,sizeof(visx)); 43 memset(visy,false,sizeof(visy)); 44 if(dfs(x))break; 45 int d = inf; 46 for(int i = 0;i < ny;i++) 47 if(!visy[i] && d > slack[i]) 48 d = slack[i]; 49 for(int i = 0;i < nx;i++) 50 if(visx[i]) 51 lx[i] -= d; 52 for(int i = 0;i < ny;i++) { 53 if(visy[i])ly[i] += d; 54 else slack[i] -= d; 55 } 56 } 57 } 58 int res = 0; 59 for(int i = 0;i < ny;i++) 60 if(linker[i] != -1) 61 res += G[linker[i]][i]; 62 return res; 63 } 64 65 int n, m; 66 67 int main() { 68 // freopen("in", "r", stdin); 69 int u, v; 70 while(~scanf("%d%d",&n,&m)) { 71 nx = ny = n; 72 memset(G, 0, sizeof(G)); 73 for(int i = 0; i < m; i++) { 74 scanf("%d%d",&u,&v); 75 G[u-1][v-1] = 1; 76 } 77 printf("%d ", nx-km()); 78 } 79 return 0; 80 }