有向环覆盖问题

转载自这里

  给你一个N个顶点M条边的带权有向图,要你把该图分成一个或多个不相交的有向环。且所有定点都被有向环覆盖。问你该有向环所有权值的总和最小是多少?

  答案就是:有向环最大权值覆盖=最优匹配。

分析:

   我们把任意一个顶点i都分成两个,即i和i’. 如果原图存在i->j的边,那么二分图有i->j’的边.

下面我们要引出几条结论:

  ① 如果原图能由多个不相交的有向环覆盖,那么二分图必然存在完备匹配。他们互为充要条件,也就是说如果二分图存在完备匹配,那么原图必定能由几个不想交的有向环覆盖. 

  ② 如果原图存在权值最大的有向环覆盖,那么二分图的最优匹配一定就是这个值。即权值最大的有向环覆盖在数值上等于改图的最优匹配值。

      因为该有向环覆盖对应了一个二分图的完备匹配,而该完备匹配的权值就等于该有向环覆盖的权值,所以最优匹配不可能丢失该最大权值的匹配。

  (假设原图的有向环为(1->2->3->1) and(6->5->4->6),那么二分图的完备匹配就是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’)

 (假设二分图的完备匹配是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’那么原图的有向环为(1->2->3->1) and (6->5->4->6))

例如HDU1853:

现在原题要求的是最小权匹配,我们把所有已知边的权值都取负数,且那些不存在的边我们取-INF(负无穷). 如果完备匹配存在,那么我们求出的最优匹配权值的绝对值 肯定<INF. 且该绝对值就是最小权值匹配.

如果完备匹配不存在,那么最优匹配权值的绝对值肯定>INF.(想想是不是) 或者这么说,如果最终求得的匹配中,有任何一个匹配边用了权值为负无穷的边,那么最优匹配不存在(即完备匹配不存在)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 #define _Clr(x, y) memset(x, y, sizeof(x))
 5 #define INF 0x3f3f3f3f
 6 #define N 1005
 7 using namespace std;
 8
 9 int mat[N][N], match[N];
10 int lx[N], ly[N];
11 int slack[N];
12 bool used_x[N], used_y[N];
13 int n, m;
14
15 bool dfs(int x)
16 {
17     used_x[x] = true;
18     for(int i=1; i<=n; i++)
19     {
20         if(used_y[i]) continue;
21         int t = lx[x] + ly[i] - mat[x][i];
22         if(t==0)
23         {
24             used_y[i] = true;
25             if(match[i]==-1 || dfs(match[i]))
26             {
27                 match[i] = x;
28                 return true;
29             }
30         }
31         else slack[i] = min(slack[i], t);
32     }
33     return false;
34 }
35
36 int KM()
37 {
38     _Clr(match, -1);
39     _Clr(ly, 0);
40     for(int i=1, j; i<=n; i++)
41         for(j=1, lx[i]=-INF; j<=n; j++)
42             lx[i] = max(lx[i], mat[i][j]);
43     for(int x=1; x<=n; x++)
44     {
45         _Clr(slack, INF);
46         while(1)
47         {
48             _Clr(used_x, 0);
49             _Clr(used_y, 0);
50             if(dfs(x)) break;
51             int d=INF;
52             for(int i=1; i<=n; i++)
53                 if(!used_y[i] && d>slack[i])
54                     d = slack[i];
55             for(int i=1; i<=n; i++)
56                 if(used_x[i])
57                     lx[i] -= d;
58             for(int i=1; i<=n; i++)
59                 if(used_y[i])
60                     ly[i] += d;
61                 else slack[i] -= d;
62         }
63     }
64     int ans=0;
65     for(int i=1; i<=n; i++)
66     {
67         if(match[i]==-1 || mat[match[i]][i]==-INF) return -1;
68         ans += mat[match[i]][i];
69     }
70     return -ans;
71 }
72 int main()
73 {
74     int m, a, b, c;
75     while(~scanf("%d%d", &n, &m))
76     {
77         for(int i=1; i<=n; i++)
78         for(int j=1; j<=n; j++)
79             mat[i][j]=-INF;
80         while(m--)
81         {
82             scanf("%d%d%d", &a, &b, &c);
83             mat[a][b] = max(mat[a][b], -c);
84         }
85         printf("%d\n", KM());
86     }
87     return 0;
88 }
时间: 2024-10-12 22:20:13

有向环覆盖问题的相关文章

Hdu 3488 Tour (KM 有向环覆盖)

题目链接: Hdu 3488 Tour 题目描述: 有n个节点,m条有权单向路,要求用一个或者多个环覆盖所有的节点.每个节点只能出现在一个环中,每个环中至少有两个节点.问最小边权花费为多少? 解题思路: 因为每个节点就出现一个,那么每个节点出度和入度都为1咯.我们可以对每个节点u拆点为u,u',分别放在集合X,Y.然后对两个集合进行完备匹配.完备匹配成功以后,每个节点就会有只有一个出度,一个入度的. 用KM求最小匹配的话,先初始化maps为-INF,然后把各边权值存为负,求出最大值取反即可. 1

HDU3488 Tour [有向环覆盖 费用流]

Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3159    Accepted Submission(s): 1525 Problem Description In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000

poj 3417 Network 【LCA】【树中增新边后 求每条树边被环所覆盖的次数】

Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4251   Accepted: 1223 Description Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNet

最小路径覆盖问题

byvoid好神啊Orz 摘自byvoid的题解 [问题分析] 有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决. [建模方法] 构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi.对于原图中存在的每条边(i,j),在二分图中连接边(Xi,Yj).然后把二分图最大匹配模型转化为网络流模型,求网络最大流. 最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数.沿着匹配边查找,就是一个路径上的点,输出所有路径即可. [建模分析] 对于一个路径覆盖,有如

费用流 hdu1853 Cyclic Tour

传送门:点击打开链接 题意:给n个点和M条有向边,要找出许多个环出来,每个环点至少有2个,所有的点都要被环覆盖1次,且只能为1次.问所有环的长度之和 这题也可以用KM来做,这里主要是练习费用流的建图 对于这题,建图也是非常的奇妙的 由于每个点的入度都是1,出度都是1 所以会想到把每个点拆分成2个点,用i和i+n来表示 然后将源点与所有的i连接起来,将汇点与所有的i+n连接起来,容量都是1 对于每一条边(u,v),添加边(u,v+n,1,cost),让第一层的点连接到第二层去 其实就是将点拆分成两

[bzoj4424]Fairy

很久之前想写这题.结果还是把握不住CF的E,太神了啊....... 首先考虑的是二分图的性质,这个so easy,图中不存在奇数环. 然后分三种情况考虑: 1.只有一个奇数环,随便删除哪条 2.多个奇数环,删除它们都覆盖的那条 3.没有奇数环,岂不是爽爆了..? 然后还引入了“返祖边”的概念,这个具体可以看博客http://blog.csdn.net/DaD3zZ/article/details/50879626  太神了反正我一点都不会 其中详细叙述了删哪条,怎么删的问题,类似于一个前缀和的思

【BZOJ4238】电压 DFS树

[BZOJ4238]电压 Description 你知道Just Odd Inventions社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”.这里简称为JOI社. JOI社的某个实验室中有着复杂的电路.电路由n个节点和m根细长的电阻组成.节点被标号为1~N 每个节点有一个可设定的状态[高电压]或者[低电压].每个电阻连接两个节点,只有一端是高电压,另一端是低电压的电阻才会有电流流过.两端都是高电压或者低电压的电阻不会有电流流过. 某天,JOI社为了维护电路

BZOJ4238电压

Description 你知道Just Odd Inventions社吗?这个公司的业务是"只不过是奇妙的发明(Just Odd Inventions)".这里简称为JOI社. JOI社的某个实验室中有着复杂的电路.电路由n个节点和m根细长的电阻组成.节点被标号为1~N 每个节点有一个可设定的状态[高电压]或者[低电压].每个电阻连接两个节点,只有一端是高电压,另一端是低电压的电阻才会有电流流过.两端都是高电压或者低电压的电阻不会有电流流过. 某天,JOI社为了维护电路,选择了一根电阻

HDU-1853 Cyclic Tour

最小权值环覆盖问题:用几个环把所有点覆盖,求所选取的边最小的权值之和. 拆点思想+求最小转求最大+KM算法 #include <cstdlib> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <fstream> #include <iostream> #define rep(i, l, r) for(i