Broadcast Stations【树形DP】

【Gym - 101667A】题目链接


  是真的一道难题啊,确实连dp应该怎样表示都没有想到。这里的突破点是N不是很大,N^{2}的复杂度是可以接受的,并且N^{2}数组的内存也是可以开下的。

  所以,这里需要列写两个dp,一个f[u][i],一个g[u][i],分别表示的是“u这个点出发,向祖先结点选距离≤i的全体和u的子树的全体结点方案的最小花费”,以及“u对u的子树下距离u点距离≥i的点的全体的点集的最小花费”。

  那么,我们先讨论一下g[ ][ ]

  • g[ ][ ]的值的求解方式:

g[u][i] += g[v][i-1],这个还是比较好理解的:

  • 最关键的f[ ][ ]的求解:

  因为要是u结点出发,包含距离为i的所有的点,那么我们可以直接是用(i + g[u][i + 1])来求f[u][i]。但是,有时候可以用已经取出来的f[v][i+1]来做这个,那么此时的答案就是(f[v][i + 1] - g[v][i] + g[u][i + 1]),其中,(f[v][i + 1] - g[v][i])指的是通过v来代替“+i”使得u向周围范围为i的结点都能被覆盖完。

  但是,这里的f[u][i]是有初始值的,不然来来回回都是0可就没有意思咯!

 

初始化:

f[u][i] = min(f[u][i+1], i + g[u][i+1]);

但是呢,i==0的时候会发生什么?值会偏小!0+g[u][1]——其实没有包含u这号点,也就是没有考虑u这个点了,所以,这里需要改一下:f[u] [0] = min(f[u][1], 1 + g[u][2])。这样就可以了。

Input1

6
1 2
3 2
2 4
5 4
6 4

Input2

12
1 2
2 3
4 5
5 3
3 6
6 7
7 8
8 9
12 9
9 11
10 9

 

My Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&( -x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define efs 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 5005;
int N, cnt, head[maxN], f[maxN][maxN] = {0}, g[maxN][maxN] = {0};
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN<<1];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
void dfs(int u, int fa)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        dfs(v, u);
        for(int j=1; j<=N; j++) g[u][j] += g[v][j-1];
    }
    g[u][0] = N; f[u][N] = N;   //g是指">=j"部分包含需要的最小花费,f是指"≤j"部分包含需要的最小花费且对于祖先包含j距离(均对子树)
    for(int j=N-1; j>=0; j--)
    {
        if(j == 0) f[u][0] = min(f[u][1], 1 + g[u][2]);
        else f[u][j] = min(f[u][j+1], j + g[u][j + 1]);
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(v == fa) continue;
            f[u][j] = min(f[u][j], f[v][j+1] - g[v][j] + g[u][j+1]);
        }
    }
    g[u][0] = f[u][0];
    for(int i=1; i<=N; i++) g[u][i] = min(g[u][i], g[u][i-1]);
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v);
    }
    dfs(1, 0);
    printf("%d\n", g[1][0]);
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值