[HDU 5313] Bipartite Graph 二分图染色+分组背包
2015-07-26 21:48
302 查看
题意:输入一个二分图,通过加边使得这张图变成一个边数最多的完全二分图. 问最多能够新加多少条边. 注意重边是不允许的.
这题数据太弱了,导致好多只染一个联通块,把剩下的点都当成孤立点计算的代码都A过去了,然而题目题目可能出现多个联通块。
思路:首先二分图可以分成两类点X和Y, 完全二分图的边数就是X * Y.我们的目的是max {X * Y}, 并且X+ Y = n,所以只要X和Y的差最小,把原图黑白染色, 每个联通块有 ai 个黑点 bi 个白点, 于是就是要确定 X 应该选择 ai 还是 bi. 然后我们考虑dp, 因为每个联通块只能选择 ai 和 bi 中的一个, 所以想到分组背包,设ai, bi为物品 重量和价值都是n/2,背包容量也是n/2,这样就使得最优解接近n/2,这里和分组背包稍微有点不同,分组背包一个组合的物品可以不选,但是这里每个联通块里面我们必须选择一个,不过如果只是dp的话可能被某些数据卡死,比如 n = 10000, m = 0 , 解决这个问题我们只要加个剪枝,判断孤立点的个数足以使得X == Y or Y+1 or Y-1。
欢迎大牛拍砖。。。
这题数据太弱了,导致好多只染一个联通块,把剩下的点都当成孤立点计算的代码都A过去了,然而题目题目可能出现多个联通块。
思路:首先二分图可以分成两类点X和Y, 完全二分图的边数就是X * Y.我们的目的是max {X * Y}, 并且X+ Y = n,所以只要X和Y的差最小,把原图黑白染色, 每个联通块有 ai 个黑点 bi 个白点, 于是就是要确定 X 应该选择 ai 还是 bi. 然后我们考虑dp, 因为每个联通块只能选择 ai 和 bi 中的一个, 所以想到分组背包,设ai, bi为物品 重量和价值都是n/2,背包容量也是n/2,这样就使得最优解接近n/2,这里和分组背包稍微有点不同,分组背包一个组合的物品可以不选,但是这里每个联通块里面我们必须选择一个,不过如果只是dp的话可能被某些数据卡死,比如 n = 10000, m = 0 , 解决这个问题我们只要加个剪枝,判断孤立点的个数足以使得X == Y or Y+1 or Y-1。
[code]#include <cmath> #include <vector> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, m; int sum1, sum2; int dp[10005]; int vis[10005]; int sta[2][10005]; vector<int> mapn[10005]; void Init(int n) { memset(vis, -1, sizeof(vis)); for(int i = 0; i <= n; i++){ mapn[i].clear(); } } void Dfs(int rt) //染色 { if(vis[rt] == -1) vis[rt] = 0; int len = mapn[rt].size(); for(int i = 0; i < len; i++){ int v = mapn[rt][i]; if(vis[v] == -1){ vis[v] = (vis[rt] ^ 1); if(vis[v] == 0) sum1++; // 统计黑点 else sum2++; // 统计白点 Dfs(v); } } } int main() { int Test; cin>>Test; while(Test--){ cin>>n>>m; Init(n); int x, y; for(int i = 0; i < m; i++){ cin>>x>>y; mapn[x].push_back(y); mapn[y].push_back(x); } int top = 0; int num = 0, num1 = 0, num2 = 0; for(int i = 1; i <= n; i++){ if(vis[i] == -1){ sum1 = 1; sum2 = 0; Dfs(i); sta[0][top] = sum1; //保存黑点 sta[1][top++] = sum2; //保存白点 if(sum2 == 0){ num++; //孤立点 } //粗略的填充完全二分图的两边,用于剪枝 else if(num1 < num2){ num1 += sum1 > sum2 ? sum1 : sum2; num2 += sum2 > sum1 ? sum1 : sum2; } else{ num2 += sum1 > sum2 ? sum1 : sum2; num1 += sum2 > sum1 ? sum1 : sum2; } } } //剪枝 if(abs(num1 - num2) <= num){ cout<<((n+1)/2)*(n/2) - m<<endl; continue; } int minn = -1; // 保存选择i个联通块后最少的结点数。 memset(dp, 0, sizeof(dp)); for(int i = 0; i < top; i++){ int minnn = 10000000000; //用于更新minn for(int j = n/2; j >= sta[0][i] || j >= sta[1][i]; j--){ int ans = 0; //dp[j-sta[0][i]] >= minn 每个联通块都必须选 if(j >= sta[0][i] && dp[j-sta[0][i]] >= minn && ans < dp[j-sta[0][i]] + sta[0][i]){ ans = dp[j-sta[0][i]] + sta[0][i]; } if(j >= sta[1][i] && dp[j-sta[1][i]] >= minn && ans < dp[j-sta[1][i]] + sta[1][i]){ ans = dp[j-sta[1][i]] + sta[1][i]; } dp[j] = ans; if(ans && ans < minnn) //更新 minnn = ans; } minn = minnn; //更新 } cout<<(dp[n/2] * (n - dp[n/2])) - m<<endl; } return 0; }
欢迎大牛拍砖。。。
相关文章推荐
- Win2008 R2显示桌面图标方法
- KM匹配模板
- java 枚举和反射 实现简单的工厂模式
- 温控结束
- Linux 使用core file文件快速定位程序崩溃代码行
- ACM常见高精度总结(java用法)
- 解决“您尝试打开的文件*.xls的格式与文件扩展名指定的格式不一致”
- intSet代码段
- EventBus使用详解(一)——初步使用EventBus
- 股票学习(K线技术--头肩形态)
- 基于Ubuntu14.04的RK3288_PopMetal Android开发环境搭建过程小记
- 简单二维点的聚类
- sax xpath读取xml字符串
- 用IE浏览器打开pdf文件出来的是空白页面,怎么办?
- Swift学习笔记(十一)——Swift中三元运算符的低级错误:Consecutive statements on a line must be separated by';'
- 每天一个小知识点14(Angularjs总结二)
- 《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录
- 关于java中的static和final的总结
- 在Hyper-V虚拟机中使用Wi-Fi上网
- 数据结构之选择排序--简单选择排序