Codeforces Round #506 (Div. 3) E. Tree with Small Distances (cf 1029E)

题目:Tree with Small Distances

题意:
给出一棵树,求最少加多少条边,使得 1 1 号节点到每个节点的距离小于等于2

思路:
比赛时做的人最少的题,赛后写了好久…
这里写图片描述
↑↑↑QωQ【绝望.jpg】

嗯这题一眼看出来是树形 dp d p ,实现上不太容易。
刚开始的思路是令 f[x][w] f [ x ] [ w ] 表示节点 x x 的上面的第w1个节点向 1 1 有连边时,要使x向下的子树满足条件时所需的最小连边数量。然后发现父节点到 1 1 的最短路径中经过了儿子节点的情况不好处理。
换了个思路,令f[x][w]表示节点 x x 的状态为w时,要使 x x 向下的子树满足条件时所需的最小连边数量。这里的状态w 0 0 时,代表x由父节点 fa f a 转移得到; w w 1时,代表 x x 由子节点y转移得到; w w 0时代表 x x 1有连边。
转移方程:
f[x][0]=min(f[y][1],f[y][2]+1) f [ x ] [ 0 ] = ∑ m i n ( f [ y ] [ 1 ] , f [ y ] [ 2 ] + 1 )
f[x][1]=min(f[y][1],f[y][2]+1)+min(f[y][2]+1min(f[y][2]+1,f[y][1])) f [ x ] [ 1 ] = ∑ m i n ( f [ y ] [ 1 ] , f [ y ] [ 2 ] + 1 ) + m i n ( f [ y ] [ 2 ] + 1 − m i n ( f [ y ] [ 2 ] + 1 , f [ y ] [ 1 ] ) )
f[x][3]=min(f[y][0],f[y][1],f[y][2]+1) f [ x ] [ 3 ] = ∑ m i n ( f [ y ] [ 0 ] , f [ y ] [ 1 ] , f [ y ] [ 2 ] + 1 )
注意 1 1 号节点和1号节点的儿子要单独判断,即 1 1 号节点的儿子的状态只能为2

给两组数据QωQ~

Input
6
1 2
2 3
3 4
4 5
5 6

Output
1

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

Output
2

代码:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define inf (1<<30)
#define maxn 200000

int n;
int f[maxn+5][5];
vector<int> a[maxn+5];
int fa[maxn+5];
int d[maxn+5];

void read(int& x) {
    scanf("%d",&x);
}

void dfs(int x) {
    for(int i=0; i<a[x].size(); i++) {
        int y=a[x][i];
        if(fa[x]==y) continue;
        fa[y]=x;
        d[x]=d[y]+1;
        dfs(y);
    }
}

int dp(int x,int w) {
    if(w==1&&a[x].size()==1) return f[x][w]=inf;
    if(f[x][w]) return f[x][w];
    int z=inf;
    for(int i=0;i<a[x].size();i++) {
        int y=a[x][i];
        if(y==fa[x]) continue;
        dp(y,0),dp(y,1),dp(y,2);
        if(w!=2) f[x][w]+=min(f[y][1],f[y][2]+1);
        else f[x][w]+=min(min(f[y][0],f[y][1]),f[y][2]+1);
        z=min(z,f[y][2]+1-min(f[y][2]+1,f[y][1]));
    }
    if(w==1) f[x][w]+=z;
    return f[x][w];
}

int main() {
    read(n);
    int frt=0;
    for(int i=1; i<n; i++) {
        int x,y;
        read(x);
        read(y);
        if(frt==0) frt=x;
        a[x].push_back(y);
        a[y].push_back(x);
    }
    dfs(1);
    int ans=0;
    for(int i=0;i<a[1].size();i++) {
        ans+=dp(a[1][i],2);
    }
    printf("%d",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值