星星之火OIer:手机网络

5 篇文章 0 订阅

题目传送门

其实这道题跟我的上一篇的三道题都挺像的

尤其是树的最大独立集

但这道题还是有本质上的差别

那三道题都是要尽量多

而这道题要尽量少

所以区别还是很大的

而且这道题的dp的定义以及状态转移方程都与那三题大不一样

首先,我们定义三种不同状态的 dp ::

  • dp [ i ] [ 0 ] ::i 节点的子树和 i 节点被全覆盖,i 节点上有塔
  • dp [ i ] [ 1 ] ::i 节点的子树和 i 节点被全覆盖,i 节点无塔
  • dp [ i ] [ 2 ] ::i 节点的子树被全覆盖,但 i 节点未被覆盖

对于这三种状态,我们分别可以得到以下三种状态转移方程::

 

dp[i][0]+=min(dp[son][0],min(dp[son][1],dp[son][2]))+1

第一种情况,他的儿子节点的每一种状态都可以转移上来,再加上他本身再架设一个

 

dp[i][1]=min(dp[i][1],dp[son][0]+sum-min(dp[son][0],dp[son][1]))

这个是最复杂的一种了。因为如果按定义来看,他至少有一个子节点是有塔的,所以。。。自己画图理解一下

P.S. sum表示覆盖子树的最小值

 

dp[i][2]+=dp[son][1]

第三种状态只能由第二个状态转移过来

 

还有就是这也是一棵无根树,需要自己定义根

(其实这个好像没有什么用,也不需要任何处理,随便从哪个节点开始都没问题)

所以我们可以随便从一个节点开始,然后dfs就行了

代码::

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
vector<int>G[10005];
int n,k,a,b,dp[10005][3];
void dfs(int x,int fa) {
    dp[x][0]=1;//本身
    dp[x][1]=1000000;//这里不要开得过大,不然一不小心就爆int然后变成负数接而就炸了
    int sum=0;//按上面说的累和
    int o=G[x].size();
    for(int i=0;i<o;i++) {
        int v=G[x][i];
        if(v==fa)//父节点
            continue;
        dfs(v,x);//从叶子节点开始逆推上来
        dp[x][2]+=dp[v][1];//按上面说的状态转移方程推过来
        dp[x][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));//同上
        sum=sum+min(dp[v][0],dp[v][1]);//同上
    }
    if(o==1&&x!=1)//只有一个儿子&&非根节点
        return;
    for(int i=0;i<o;i++) {
        int v=G[x][i];
        if(v==fa)
            continue;
        dp[x][1]=min(dp[x][1],dp[v][0]+sum-min(dp[v][0],dp[v][1]));//同上上
    }
}
int main() {
    read(n);
    for(int i=1;i<n;i++)
        read(a),read(b),G[a].push_back(b),G[b].push_back(a);
    dfs(1,0);
    pr(min(dp[1][0],dp[1][1]));//读入+dfs+输出
}

大概就是这样,不懂的可以评论讨论

更多作者博客在这里

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值