• [CTSC 2008] 祭祀


    [题目链接]

             https://www.lydsy.com/JudgeOnline/problem.php?id=1143

    [算法]

              答案为最小路径可重复点覆盖所包含的路径数,将原图G进行弗洛伊德传递闭包,得到一张新图G',然后求出拆点二分图G2'的最大匹配,N - 最大匹配 即为答案,我们尝试证明上述结论 :

              设祭祀点集合为S,最小路径可重复点覆盖的边集为Path,由于Path覆盖了所有节点,故每条路径上至多选一个点,有 : |S| <= |Path| , 因此,如果我们能构造出一组解,使得| S | = | Path | , 就证明了此结论,这里给出一种构造方案 :

              首先求出拆点二分图的最大匹配,设节点x在拆点二分图上分别对应左部节点x和右部节点x' ,  对于每个非匹配节点x0,我们不断访问 x0,match[x0'],match[ match[x0'] ] .. 直到最后遇到一个左部节点y0,使得其右部点y0'为非匹配点, 那么就得到了一条路径, 其中y0为起点,x0为

              终点,求出这样的所有路径,就得到了| Path |的一种方案,且所有路径不相交,我们现在要将| Path |集合中的每条路径选出一个节点,构成集合| S |

              首先我们将所有路径的终点构成一个集合E,根据传递闭包的性质,两个祭祀点之间无路径相连,等价于在新图G’上任意两个祭祀点之间没有边,不妨让集合E中的每个节点走一条边,构成集合Next(E),如果E和Next(E)的交集为空集,则S = E

              否则,对于交集中的每个点e,我们沿着e所在的路径不断向上移动,直到e不在当前的交集中,从E中删除e,加入e',重复以上过程,直到交集为空,就求出了S的一种组成方案

              可以证明,在任何时刻,我们都能找到合法的e',因为若没有,说明e所在的路径上所有点都可以被其他路径上的点到达,我们可以找到到达e所在的的路径起点的那条路径,将其延伸,使得| Path | 减少1,并覆盖所有节点,与Path的最小性矛盾

              综上所述,答案即为最小路径可重复点覆盖所包含的路径数

       [代码]

               

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 210
    
    int i,j,k,n,m,u,v,ans;
    bool g[MAXN][MAXN],mp[MAXN][MAXN];
    bool visited[MAXN];
    int match[MAXN];
    
    inline bool hungary(int u)
    {
            int v;
            for (v = 1; v <= n; v++)
            {
                    if (mp[u][v] && !visited[v])
                    {
                            visited[v] = true;
                            if (!match[v] || hungary(match[v]))
                            {
                                    match[v] = u;
                                    return true;
                            }
                    }
            }
            return false;
    }
    
    int main() 
    {
            
            scanf("%d%d",&n,&m);
            for (i = 1; i <= n; i++) g[i][i] = true;
            for (i = 1; i <= m; i++)
            {
                    scanf("%d%d",&u,&v);
                    g[u][v] = true;
            }
            for (k = 1; k <= n; k++)
            {
                    for (i = 1; i <= n; i++)
                    {
                            for (j = 1; j <= n; j++)
                            {
                                    g[i][j] |= g[i][k] & g[k][j];
                            }
                    }
            }
            for (i = 1; i <= n; i++)
            {
                    for (j = 1; j <= n; j++)
                    {
                            if (i != j && g[i][j])
                               mp[i][j] = true;
                    }
            }
            ans = n;
            for (i = 1; i <= n; i++)
            {
                    memset(visited,false,sizeof(visited));
                    if (hungary(i)) ans--;
            }
            printf("%d
    ",ans);
            
            return 0;
        
    }

         

  • 相关阅读:
    Google Kubernetes设计文档之服务篇-转
    基于kubernetes构建Docker集群管理详解-转
    Pass云Docker介绍
    Spring <context:annotation-config/> 解说
    webapp开发需要注意的浏览器内核知识
    koala编译scss文件时不支持中文字体的解决方案
    CSS3硬件加速需要注意的事项
    ios客户端快速滚动和回弹效果的实现
    mui禁止滚动条和禁止滚动
    苹果端禁用左右滑动屏幕返回上级页面
  • 原文地址:https://www.cnblogs.com/evenbao/p/9418628.html
Copyright © 2020-2023  润新知