树形dp ---- gym101667 A(贪心 + 树形dp + 两个dp方程组维护)

题目链接


题目大意:

就是一棵 5 e 3 5e3 5e3的树,可以选择一些点,放上基站,如果 u u u上的基站价值为 d d d,那么距离 u u u小于等于 d d d的点都会被覆盖,问使得整棵树被覆盖需要的最小价值。


解题思路:

  1. 一开始我是这么想树形dp的 d p [ i ] [ j ] 表 示 覆 盖 完 i 子 树 并 且 i 节 点 放 置 权 值 为 j 的 覆 盖 站 的 所 需 要 的 最 小 价 值 dp[i][j]表示覆盖完i子树并且i节点放置权值为j的覆盖站的所需要的最小价值 dp[i][j]iij

  2. 但是这里有两个问题就是如果你是有根树:你每个点的放置一个权值为 w w w的信号站,不仅会覆盖到儿子节点,也会覆盖到祖先节点,这样 d p dp dp不容易实现,因为你不知道它向上影响了多远

  3. 那么我们为什么不把那个影响距离带到 d p dp dp方程里面去呢?

  4. d p [ u ] [ j ] 表 示 覆 盖 完 u 这 个 子 树 并 且 能 够 覆 盖 到 所 有 向 上 距 离 它 ≤ j 所 有 点 的 最 小 代 价 是 多 少 ? dp[u][j]表示覆盖完u这个子树并且能够覆盖到所有向上距离它\leq j所有点的最小代价是多少? dp[u][j]uj

  5. 那么我们知道向上影响距离是 j j j无非两种情况就是
    (1):在u这个位置直接放置权值为 j j j的基站
    (2):子树传递影响上去

  6. 对于子树直接放置权值为 j j j的基站我们知道它的影响是两个方向的有儿子也有祖先:那么根据贪心原理 d p [ u ] [ j ] = j + g [ u ] [ j + 1 ] , g [ u ] [ j + 1 ] : 覆 盖 距 离 u 距 离 ≥ j 的 所 有 dp[u][j]=j+g[u][j+1],g[u][j+1]:覆盖距离u距离\ge j的所有 dp[u][j]=j+g[u][j+1]g[u][j+1]:uj儿子节点所需要的最小代价(注意这里是只有儿子)

  7. 如果是子树传递上去的呢?
    d p [ u ] [ j ] = m i n { d p [ s o n u ] [ j + 1 ] + ( g [ u ] [ j + 1 ] − g [ s o n u ] [ j ] ) } dp[u][j]=min\{dp[son_u][j+1]+(g[u][j+1]-g[son_u][j])\} dp[u][j]=min{dp[sonu][j+1]+(g[u][j+1]g[sonu][j])}
    为啥要减掉 g [ s o n u ] [ j ] g[son_u][j] g[sonu][j]因为贡献重复计算了

  8. 那么 g g g数组怎么算呢?很好算的 g [ u ] [ i ] + = g [ s o n u ] [ i − 1 ] g[u][i]+=g[son_u][i-1] g[u][i]+=g[sonu][i1]

  9. 注意点1:就是 d p [ u ] [ 0 ] = 0 + g [ u ] [ 1 ] dp[u][0]=0+g[u][1] dp[u][0]=0+g[u][1]这是不合法的因为实际上并没有包含到 u u u节点,那么 d p [ u ] [ 0 ] dp[u][0] dp[u][0]只能算第二种情况就是子树传递贡献上去!!

  10. 注意点2:就是实际上 d p [ u ] [ i + 1 ] dp[u][i+1] dp[u][i+1]是对 d p [ u ] [ i ] dp[u][i] dp[u][i]有贡献影响的,因为覆盖范围 ≥ i + 1 \ge i+1 i+1肯定也 ≥ i \ge i i, g g g数组同理也是 g [ u ] [ i − 1 ] g[u][i-1] g[u][i1] g [ u ] [ i ] g[u][i] g[u][i]是用贡献的因为距离 ≤ i − 1 \leq i-1 i1肯定也 ≤ i \leq i i

  11. 注意点3 g [ u ] [ 0 ] = d p [ u ] [ 0 ] g[u][0]=dp[u][0] g[u][0]=dp[u][0]


AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 5010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
int n, lim = 5000;
vector<int> G[maxn];
int dp[maxn][maxn]; // dp[u][j] 表示覆盖完u这个子树并且覆盖了上面所有距离<=j的祖先节点最小费用
int g[maxn][maxn]; // g[u][i] 表示覆盖了距离u >= i的所有节点所需要的最小费用
void dfs(int u, int fa) {
   int min_son = -1;
   for(auto it : G[u]) {
      if(it == fa) continue;
      dfs(it,u);
      for(int i = 1; i <= lim; ++ i) g[u][i] += g[it][i-1];
      if(min_son == -1 || dp[it][1] < dp[min_son][1])
         min_son = it;
   }
   
   if(G[u].size() == 1 && u != 1) dp[u][0] = 1; // 叶子节点为1
   else dp[u][0] = dp[min_son][1] + g[u][1] - g[min_son][0];

   dp[u][lim] = INF;// 把边界设置成无穷大
   for(int i = lim - 1; i >= 1; -- i) {
      dp[u][i] = g[u][i+1] + i;
      for(auto it : G[u]) 
         if(it != fa) {
            dp[u][i] = min(dp[it][i+1]-g[it][i]+g[u][i+1],dp[u][i]);
         }
   } 
   // 处理一下同层之间的贡献
   for(int i = lim-1; i >= 0; -- i) dp[u][i] = min(dp[u][i],dp[u][i+1]); 
   g[u][0] = dp[u][0]; // g的边界
   for(int i = 1; i <= lim; ++ i) g[u][i] = min(g[u][i],g[u][i-1]);
}

int main() {
   IOS;
   cin >> n;
   for(int i = 1; i < n; ++ i) {
      int u, v;
      cin >> u >> v;
      G[u].push_back(v);
      G[v].push_back(u);
   }   
   dfs(1,0);
   cout << g[1][0];
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值