首页 > 代码库 > 二分图类题目集合

二分图类题目集合

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

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

第四届“恒生杯”程序设计大赛决赛 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

 

二分图类题目集合