• LA 2038 Strategic game(最小点覆盖,树形dp,二分匹配)


    题意即求一个最小顶点覆盖。

    对于没有孤立点的图G=(V,E),最大独立集+最小顶点覆盖= V。(往最大独立集加点)

    问题可以变成求树上的最大独立集合。

    每个结点的选择和其父节点选不选有关,

    dp(u,1)表示父节点选,这时u不可选,

    dp(u,0)表示父节点不选,这时u可选可不选。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1501;
    int meo[maxn][2];
    int vis[maxn][2], clk;
    int hd[maxn],nx[maxn<<1],to[maxn<<1],ec;
    
    void add(int u,int v)
    {
        to[ec] = v;
        nx[ec] = hd[u];
        hd[u] = ec++;
    }
    
    int dp(int u,int a = 0,int f = -1)//a表示父节点选不选
    {
        if(vis[u][a] == clk) return meo[u][a];
        vis[u][a] = clk;
        int &re = meo[u][a];
        re = 0;
        int pick = 1;
        for(int i = hd[u]; ~i; i = nx[i]){
            int v = to[i];
            if(v == f) continue;
            if(!a){
                pick += dp(v,1,u);
            }
            re += dp(v,0,u);
        }
        if(!a) re = max(re,pick);
        return re;
    }
    
    //#define LOCAL
    int main()
    {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif
        int n;
        while(~scanf("%d",&n)){
            memset(hd,-1,sizeof(hd)); ec = 0;
            for(int i = 0; i < n; i++){
                int u,sn; scanf("%d:(%d)",&u,&sn);
                while(sn--){
                    int v;scanf("%d",&v);
                    add(u,v); add(v,u);
                }
            }
            clk++;
            printf("%d
    ",n-dp(0));
        }
        return 0;
    }

    最小点覆盖还可以用二分匹配来做

    关键代码,下面可以适合无向图,如果用有向图的算法一个匹配会算两次。

    int link[maxn];
    int vis[maxn], clk;
    
    bool aug(int u)
    {
        if(vis[u] == clk) return false;
        vis[u] = clk;
        for(int i = hd[u]; ~i; i = nx[i]){
            int v = to[i];
            if(!~link[v] || aug(link[v])){
                link[v] = u;
                link[u] = v;
                return true;
            }
        }
        return false;
    }
    
    int Hungary(int n)
    {
        memset(link,-1,sizeof(link));
        int ans = 0;
        for(int i = 0; i < n; i++){
            if(link[i]<0){
                clk++;
                if(aug(i)) ans++;
            }
        }
        return ans;
    }

    也可以直接dp求最小点覆盖集合,

    f[u][p]表示以u为根的树最小点覆盖,p表示选不选u。

    当选u的时候,子结点可选可不选,

    当不选u的时候,子结点都选。

    (实际上存在在某些结点只选一个子节点的最优解的情况,但是这样做并不会丢解)

    int f[maxn][2],vis[maxn],clk;
    
    void dfs(int u = 0,int fa = -1)
    {
        vis[u] = clk;
        f[u][0] = 0; f[u][1] = 1;
        for(int i = hd[u]; ~i; i = nx[i]){
            int v = to[i];
            if(v == fa) continue;
            if(vis[v] != clk) dfs(v,u);
            f[u][1] += min(f[v][0],f[v][1]);
            f[u][0] += f[v][1];
        }
    }
  • 相关阅读:
    Scala 插入排序
    win10下安装使用mysql-8.0.18
    Autofac注入多数据库(DbContext)
    缓存一致性问题以及方案(一) Redis
    Java编程中,一些好的习惯从一开始就值得坚持
    IDEA项目无法引用本地Class类,引用路径正确但报错标红
    git怎么撤销已经push到远端的commit?
    git的命令大全及如何修改git账号和提交的邮箱和用户名
    renren-fast-vue无法运行相关问题解决办法n ./src/assets/scss/index.scss Module build failed: Error: ENOENT: no su
    VsCode系列(一):下载安装及汉化
  • 原文地址:https://www.cnblogs.com/jerryRey/p/4854823.html
Copyright © 2020-2023  润新知