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