此文用来总结二分图相关知识的题目,以后遇到了就囤在一起总结吧。
最近遇到的一道最小路径覆盖的题目,其实很裸,忘了也没看出来就是最小路径覆盖。
http://acm.hust.edu.cn/problem/show/1643
题意:有n个物品,每一天只能选取一些满足条件的物品,即选取的物品相邻之间要满足abs(a[i]-a[j]) >= k,问最少多少天能够选完所有物品。
分析:首先建立一个图,对于abs(a[i]-a[j])>=k的i,j(i < j)建一条i->j又向边,然后就是直接最小路径覆盖了。
每个点拆成2点i,i',分别表示起点和终点,对于i->j的边,拆点后连i->j'的边,总点数n-二分图最大匹配数就是答案。
可以简单证明一下,二分图每一次匹配一条边,相当于在原图中走一步,那么我们的目标是一直走下去,走最少次数将所有的点访问完,而且每个点只能走一次。
原图对每个点走一次,最多走n次便可,现在匹配一条边后需要n-1次,这样最大匹配k条边,说明原图最少需要n-k次。最大匹配中每个点最多与一条匹配边相关联实际上就能表示,每个点只能访问一次。
代码:
1 #include <bits/stdc++.h> 2 #define in freopen("data.txt", "r", stdin); 3 #define bug(x) printf("Line %d:>>>>>>>>>>\n", (x)); 4 5 using namespace std; 6 const int maxn = 333; 7 int vis[maxn], link[maxn], g[maxn][maxn]; 8 int a[maxn], n; 9 bool dfs(int x) { 10 for(int y = 1; y <= n; y++) if(g[x][y]) { 11 if(vis[y]) continue; 12 vis[y] = 1; 13 if(link[y] == 0 || dfs(link[y])) { 14 link[y] = x; 15 return true; 16 } 17 } 18 return false; 19 } 20 int main() { 21 22 int T, k; 23 for(int t = scanf("%d", &T); t <= T; t++) { 24 scanf("%d%d", &n, &k); 25 memset(link, 0, sizeof link); 26 memset(g, 0, sizeof g); 27 for(int i = 1; i <= n; i++) 28 scanf("%d", a+i); 29 for(int i = 1; i <= n; i++) 30 for(int j = i+1; j <= n; j++) 31 if(abs(a[i]-a[j]) >= k) g[i][j] = 1; 32 int ans = 0; 33 for(int i = 1; i <= n; i++) { 34 memset(vis, 0, sizeof vis); 35 if(dfs(i)) 36 ans++; 37 } 38 cout << n-ans << endl; 39 } 40 return 0; 41 }