博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ZOJ-2342 Roads 二分图最小权值覆盖
阅读量:6659 次
发布时间:2019-06-25

本文共 3197 字,大约阅读时间需要 10 分钟。

题意:给定N个点,M条边,M >= N-1。已知M条边都有一个权值,已知前N-1边能构成一颗N个节点生成树,现问通过修改这些边的权值使得最小生成树为前N条边的最小改动总和为多少?

分析:由于计算的最小改动且为最小生成树则显然前N-1条边肯定权值都减少,后面的边权值都增加。由于选择的边为前N-1得到最小生成树,因此首先将N-1条边构图,然后对后面的每一条边,那么这条边所构成的环中,有任意一条边的a与该边b,设原始权重为w[a],w[b],改变量为d[a],w[b],则有w[a] - d[a] <= w[b] + d[b],移项后有w[a] - w[b] <= d[a] + d[b],而我们要求的 d[] 数组的和值的最小值。可以将所有的 d[x] 看作是x节点(边)的权值,则此题相当于给每个节点(边)分配一个权值,使得满足所有环中的不等式,也就是求一个最小权值匹配,其对偶问题就是构图之后的最大权值匹配问题,可行标的值就是需要分配的权值。本题有一个隐含的条件就是d[i] + d[j] >= 0表现在图中就是每个点对之间连了一条权值为0的边,这样才能够保证求出来的值都是正值。

简要分析其原理:首先最大权值匹配算法的初始化就是选择的最大的边设定可行标,且算法在始终不会改变已经匹配边的左右可行标之和的情况下,不停的扩充权值更小的边进入子图,因此其最后得到的最大匹配一定能够保证所有的边左右端点可行标之和大于等于该边权值。其次,如果一个匹配不是最大匹配,那么既然存在最大匹配那么说明肯定有某条本应该在最大匹配中的边没有加入到子图中且该边的左右端点可行标之和小于该边的权值,否则与假设矛盾。

#include 
#include
#include
#include
using namespace std;const int N = 65;const int M = 405;const int inf = 0x3f3f3f3f;int cn[N][N]; // 原图中的边int w[M][M]; // 二分图中边的关系int lx[M], ly[M];char vx[M], vy[M];int match[M];int slack[M];int n, m, nm;struct Edge { int a, b, c;}e[M];bool dfs(int p, int u, int id) { for (int i = 1; i <= n; ++i) { if (i == p) continue; if (cn[u][i] == id) return true; if (cn[u][i] != 0 && dfs(u, i, id)) { w[cn[u][i]][id-(n-1)] = max(e[cn[u][i]].c - e[id].c, 0); return true; } } return false;}bool path(int u) { vx[u] = 1; for (int i = 1; i <= nm; ++i) { if (vy[i]) continue; if (lx[u] + ly[i] == w[u][i]) { vy[i] = 1; if (match[i] == -1 || path(match[i])) { match[i] = u; return true; } } else { slack[i] = min(slack[i], lx[u]+ly[i]-w[u][i]); } } return false;}void KM() { memset(match, 0xff, sizeof (match)); memset(ly, 0, sizeof (ly)); memset(lx, 0x80, sizeof (lx)); for (int i = 1; i <= nm; ++i) { for (int j = 1; j <= nm; ++j) { lx[i] = max(lx[i], w[i][j]); } } for (int i = 1; i <= nm; ++i) { int cnt = 0; while (1) { memset(slack, 0x3f, sizeof (slack)); memset(vx, 0, sizeof (vx)); memset(vy, 0, sizeof (vy)); if (path(i)) break; int d = inf; for (int j = 1; j <= nm; ++j) if (!vy[j]) d = min(d, slack[j]); for (int j = 1; j <= nm; ++j) if (vx[j]) lx[j] -= d; for (int j = 1; j <= nm; ++j) { if (vy[j]) ly[j] += d; else slack[j] -= d; } } }}int main() { int T, a, b, c; scanf("%d", &T); while (T--) { scanf("%d %d", &n, &m); memset(cn, 0, sizeof (cn)); memset(w, 0, sizeof (w)); for (int i = 1; i <= m; ++i) { scanf("%d %d %d", &a, &b, &c); if (i < n) cn[a][b] = cn[b][a] = i; e[i].a = a, e[i].b = b, e[i].c = c; } for (int i = n; i <= m; ++i) { cn[e[i].b][e[i].a] = i; dfs(0, e[i].a, i); cn[e[i].b][e[i].a] = 0; } nm = max(n-1, m-(n-1)); KM(); for (int i = 1; i < n; ++i) printf("%d\n", e[i].c-lx[i]); for (int i = 1; i <= m-(n-1); ++i) printf("%d\n", e[i+n-1].c+ly[i]); } return 0;}

 

转载地址:http://eyqto.baihongyu.com/

你可能感兴趣的文章
day10-linux查找find命令 介绍
查看>>
Netbeans 8.1启动参数配置
查看>>
PostgreSQL9.6 服务器参数配置
查看>>
eclipse maven添加本地仓库的jar包
查看>>
Linux基础知识题解答(二)
查看>>
Scenario 4 – HP C7000 Virtual Connect flexFabric SUS with Active/Active
查看>>
Linux(RHEL7及CentOS7)下DNS服务器的搭建与配置
查看>>
Mysql5.6 show slave hosts 发现数据库配置参数异常
查看>>
我的JavaScript使用心得
查看>>
电信服务业是民营资本的下一个金矿
查看>>
短信行业已夕阳?“云片”说那得看是在什么领域里
查看>>
OpenStack 搭建(二)
查看>>
JAVA常见算法题(二)
查看>>
CentOS 6 自定义单实例 二进制方式 安装mariadb-5.5.59
查看>>
操作显存数据(1601)
查看>>
如何测试WEB应用程序防止SQL注入攻击
查看>>
rsync+sersync实战
查看>>
Step by Step-构建自己的ORM系列-开篇
查看>>
运维那点事
查看>>
Java数组冒泡排序与二维数组
查看>>