二分图类题目集合

此文用来总结二分图相关知识的题目,以后遇到了就囤在一起总结吧。

最近遇到的一道最小路径覆盖的题目,其实很裸,忘了也没看出来就是最小路径覆盖。

第四届“恒生杯”程序设计大赛决赛 A

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 }
View Code

 

你可能感兴趣的:(二分图)