HDU 2853 Assignment (KM算法)
2015-08-13 22:16
447 查看
来自网上的巧妙思路:
因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。
根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。
全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些变就 有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。
因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。
根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。
全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些变就 有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。
//#pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; typedef long long LL; const long long mod = 1e9 + 7; const int INF = 0x3f3f3f3f; const int M = 305; /*求最大权匹配 若求最小全匹配,可将权值取相反数,结果取相反数*/ int nx, ny; //两边的点数 int g[M][M]; //二分图描述 int link[M], lx[M], ly[M]; //y中各点匹配状态,x,y中的点编号 int slack[M]; bool visx[M], visy[M]; bool dfs(int x) { visx[x] = 1; for (int y = 1; y<=ny; y++) { if (visy[y]) continue; int tmp = lx[x] + ly[y] - g[x][y]; if (!tmp) { visy[y] = 1; if (link[y] == -1 || dfs(link[y])) { link[y] = x; return 1; } } else if (slack[y]>tmp) //不在相等子图中slack 取最小的 { slack[y] = tmp; } } return 0; } int KM() { memset(link, -1, sizeof(link)); memset(ly, 0, sizeof(ly)); for (int i = 1; i<=nx; ++i) //lx,ly为顶标,nx,ny分别为x点集y点集的个数 { lx[i] = -INF; for (int j = 1; j<=ny; ++j) { if (g[i][j]>lx[i]) { lx[i] = g[i][j]; } } } for (int x = 1; x<=nx; ++x) { for (int i = 1; i<=ny; ++i) { slack[i] = INF; } while (1) { memset(visx, 0, sizeof(visx)); memset(visy, 0, sizeof(visy)); if (dfs(x)) //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广 break; //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。 //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d, //所有在增广轨中的Y方点的标号全部加上一个常数d int d = INF; for (int i = 1; i<=ny; ++i) { if (!visy[i] && d>slack[i]) d = slack[i]; } for (int i = 1; i<=nx; ++i) { if (visx[i]) lx[i] -= d; } for (int i = 1; i<=ny; ++i) //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d { if (visy[i]) ly[i] += d; else slack[i] -= d; } } } int res = 0; for (int i = 1; i<=ny; ++i) { if (link[i] != -1) res += g[link[i]][i]; } return res; } int main() { int n,m; while (~scanf("%d%d", &n, &m)) { memset(g,0,sizeof(g)); for (int i = 1; i<=n; ++i) { for (int j = 1; j<=m; ++j) { scanf("%d", &g[i][j]); g[i][j] *= 100; } } int ans=0; for (int i = 1; i<=n; ++i) { int tmp; scanf("%d",&tmp); ans+=g[i][tmp]; g[i][tmp]+=1; } nx=n; ny=m; int res=KM(); printf("%d %d\n", n - res % 100, res / 100 - ans / 100); } return 0; }
相关文章推荐
- Linux时间子系统之二:表示时间的单位和结构
- Java基础——File类使用时的小细节
- URAL 1779 F - The Great Team 构造
- (大数据工程师学习路径)第三步 Git Community Book----基本用法(下)
- [我们是这样理解语言的-3]神经网络语言模型(续)
- HDU 4265(Science!-二分网络流)
- FQA 之线程相关
- HTTP 方法:GET 对比 POST 转自w3school
- 系统架构师考试——程序计数器 PC, 指令寄存器IR、状态寄存器SR、通用寄存器GR
- coj 1342: Double
- 【Effective C++ 3rd 心得、归纳、实践】 Item 12: 拷贝一个对象的所有组成部分
- LNAMP源码安装整合加论坛及动静分离
- [我们是这样理解语言的-3]神经网络语言模型
- networkx笔记:绘制基本网络图
- 动态规划——F 最大矩阵和
- OC_id类型
- JAVA基础一大堆0813Web项目
- ptmalloc
- onfocus事件
- HDU 1542 Atlantis (线段树 + 扫描线 + 离散化)