有向环覆盖问题

转载自这里

 

  给你一个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 }
View Code

 

转载于:https://www.cnblogs.com/khan724/p/4379477.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
棋盘覆盖问题是指在一个 $2^n \times 2^n$ 的棋盘上,恰好有一个方格是缺失的,用 L 形骨牌覆盖每个方格,使得每个 L 形骨牌恰好覆盖三个方格,要求图案连通且不重叠。下面介绍四种具体实现方法: 方法一:分治法 这是一种自顶向下的递归方法,假设缺失的方格在左上角,将棋盘分成四个 $2^{n-1}\times 2^{n-1}$ 的子棋盘,缺失的方格必定在其中一个子棋盘中,用一块 L 形骨牌填补这个子棋盘中心的空格。然后递归地处理另外三个子棋盘。 用 C 语言实现步骤如下: ``` void chessboard_covering(int n, int x0, int y0, int x, int y, int (*A)[MAXN]) { if (n == 1) return; int t = ++tile; int m = n / 2; if (x < x0 + m && y < y0 + m) chessboard_covering(m, x0, y0, x, y, A); else { A[x0+m-1][y0+m-1] = t; chessboard_covering(m, x0, y0, x, y, A); } if (x < x0 + m && y >= y0 + m) chessboard_covering(m, x0, y0+m, x, y, A); else { A[x0+m-1][y0+m] = t; chessboard_covering(m, x0, y0+m, x, y, A); } if (x >= x0 + m && y < y0 + m) chessboard_covering(m, x0+m, y0, x, y, A); else { A[x0+m][y0+m-1] = t; chessboard_covering(m, x0+m, y0, x, y, A); } if (x >= x0 + m && y >= y0 + m) chessboard_covering(m, x0+m, y0+m, x, y, A); else { A[x0+m][y0+m] = t; chessboard_covering(m, x0+m, y0+m, x, y, A); } } ``` 方法二:随机化算法 这种方法直接随机选取一块 L 形骨牌覆盖一个空白格子,并将相邻的格子涂成另外两个颜色,然后递归处理。选取骨牌和涂色方案是随机的,一般情况下需要多次尝试才能得到正确的结果。 方法三:DLX算法 DLX(Dancing Links X)算法是一种高效的搜索算法,可用于解决精确覆盖问题。将棋盘的每个方格和骨牌都看成一个节点,在节点间建立一个十字链表,每种骨牌对应的节点连成一个。将缺失的格子看成一个特殊的列,将其与能够覆盖它的骨牌所对应的行连成一个。然后从十字链表中依次选择骨牌进行覆盖,并删去相应的列和行。如果成功地删掉了所有列,那么算法结束,否则回溯。 方法四:Z 形法 该算法也称为预处理法,是一种面向具体问题的优化方法。通过对特定规模下的棋盘的覆盖情况进行预处理,然后通过查表的形式得到结果,省去构造和搜索的过程。 用 C 语言实现棋盘覆盖问题的 Z 形法步骤如下: ``` const int MAXN = 1024; int A[MAXN][MAXN]; void init() { int m = 1, n = 1, t = 0; A[0][0] = A[0][1] = A[1][0] = 1; A[1][1]=++t; while (m < n || m < MAXN && n < MAXN) { for (int i = 0; i < m; ++i) for (int j = 0; j < m; ++j) { A[n+j][m+i] = A[j][i] + t * m * m; A[m+j][n+m-i-1] = A[j][i] + t * m * m * 2; A[n+m-i-1][n+m-j-1] = A[j][i] + t * m * m * 3; } m *= 2, n += m, t = A[m-1][m-1]; } } int main() { init(); int n; while (scanf("%d", &n) == 1) { int m = 1; while (m < n) m *= 2; for (int i = 0; i < m; ++i) { for (int j = 0; j < m; ++j) printf("%3d", A[i][j] < n*n ? A[i][j] : -1); printf("\n"); } } return 0; } ``` 注意事项:表格难以在markdown中结构化,如需查看代码,可在其他编译器中编译。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值