• 逐个击破


    传送门

    一道很好的树形DP!

    这道题需要分类讨论。我们用dp[i][0]表示以i为根的子树内没有敌人的最小花费,dp[i][1]表示以i为根的子树内有敌人的最小花费,根据题目描述,合法情况只能有一个敌人。至于有没有敌人,我们可以通过先记录一下哪里有敌人,之后进行一下按位或。

    考虑当前如果根节点有敌人,那么显然dp[i][0]就是INF(不合法),而dp[i][1]的话,就要把所有有敌人的子树全部切断,也就是取min(dp[v][0],dp[v][1] + e[i].v)

    如果当前根节点没有敌人,那么dp[i][0]就和上面的dp[i][1]转移是一样的,而dp[i][1],我们就允许保留一个不切断,我们还是一样先转移,之后枚举一遍保留dp[v][1]的最小值即可。(也就是我们相当于减去原来切断的花费,加上保留的花费)

    之后就完成啦。(%%%prophetB)看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<set>
    #include<queue>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 100005;
    const int N = 20005;
    const ll INF = 100000000000009;
    const double eps = 1e-4;
    
    int read()
    {
       int ans = 0,op = 1;
       char ch = getchar();
       while(ch < '0' || ch > '9')
       {
          if(ch == '-') op = -1;
          ch = getchar();
       }
       while(ch >= '0' && ch <= '9')
       {
          ans *= 10;
          ans += ch - '0';
          ch = getchar();
       }
       return ans * op;
    }
    
    struct edge
    {
       int next,to,v;
    }e[M<<1];
    
    int n,k,x,head[M],ecnt,y,z;
    ll dp[M][2],ans;
    bool vis[M],col[M];
    
    void add(int x,int y,int z)
    {
       e[++ecnt].to = y;
       e[ecnt].v = z;
       e[ecnt].next = head[x];
       head[x] = ecnt;
    }
    
    void dfs(int x,int fa)
    {
       col[x] = vis[x];
       ll tot = 0,minn = INF;
       for(int i = head[x];i;i = e[i].next)
       {
          if(e[i].to == fa) continue;
          dfs(e[i].to,x);
          col[x] |= col[e[i].to],tot += min(dp[e[i].to][0],dp[e[i].to][1] + e[i].v);
          if(col[e[i].to]) minn = min(minn,dp[e[i].to][1] - min(dp[e[i].to][0],dp[e[i].to][1] + e[i].v));
       }
       if(vis[x]) dp[x][0] = INF,dp[x][1] = tot;
       else dp[x][0] = tot,dp[x][1] = tot + minn;
    }
    
    int main()
    {
       n = read(),k = read();
       rep(i,1,k) x = read() + 1,vis[x] = 1;
       rep(i,1,n-1) x = read() + 1,y = read() + 1,z = read(),add(x,y,z),add(y,x,z);
       dfs(1,1);
       printf("%lld
    ",min(dp[1][0],dp[1][1]));
       return 0;
    }
  • 相关阅读:
    oracle 开发 第16章 SQL优化
    oracle 开发 第13章 数据库对象
    oracle 开发 第14章 集合
    oracle 开发 第15章 大对象
    IDEA创建springboot项目失败问题解决
    redis事务 学习笔记
    redis通信协议 学习笔记
    运行报caused by: redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to host错
    redis主从同步(复制+哨兵) 学习笔记
    redis限流redis-cell模块安装 笔记
  • 原文地址:https://www.cnblogs.com/captain1/p/9930340.html
Copyright © 2020-2023  润新知