poj 3659 Cell Phone Network USACO 树形DP

传送门


题目大意:
给出一颗n个节点的树(也就是有n-1条边),请选出最小点数使得所有点被覆盖掉(每个节点可以被自己覆盖,也可以被父亲覆盖,还可以被儿子覆盖)


分析:
好难啊( ⊙ o ⊙ )啊!
f[i][0]代表选择i节点,那么i和i的儿子都被覆盖
f[i][1]代表不选i节点,但是i被儿子覆盖
f[i][2]代表不选i节点,并且i不被儿子覆盖,但是儿子已经被覆盖
f[i][0]+=min(f[to[i]][0],f[to[i]][1],f[to[i]][2])
f[i][2]+=min(f[to[i]][0],f[to[i]][1])
这两个很容易理解
那么f[i][1]怎么转移呢(⊙o⊙)?
我们要保证至少有一个儿子是选择的
所以我们选择k点
f[i][1]=f[k][0]+sum((f[to[i]!=k][1],f[to[i]!=k][0]))
然后O(n)的枚举k点即可O(∩_∩)O哈!


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
#define MIN(a,b,c) min(a,min(b,c))
using namespace std;
const int maxn=10000+5;
int n,hd[maxn],to[maxn*2],nxt[maxn*2],cnt,f[maxn][3];
void add(int x,int y){
    to[cnt]=y;
    nxt[cnt]=hd[x];
    hd[x]=cnt++;
}
void dfs(int root,int fa){
    int sum=0;
    f[root][0]=1;//root is chosen and root&root's son are covered
    f[root][1]=inf;//root is not chosen and root&root's son are covered
    f[root][2]=0;//root is not chosen and root si not covered ,root's son is covered
    for(int i=hd[root];i!=-1;i=nxt[i]){
        if(to[i]==fa)
            continue;
        dfs(to[i],root),sum+=min(f[to[i]][1],f[to[i]][0]);
        f[root][0]+=MIN(f[to[i]][0],f[to[i]][1],f[to[i]][2]);
        f[root][2]+=min(f[to[i]][0],f[to[i]][1]);
    }
    for(int i=hd[root];i!=-1;i=nxt[i]){
        if(to[i]==fa)
            continue;
        f[root][1]=min(f[root][1],f[to[i]][0]+sum-min(f[to[i]][0],f[to[i]][1]));
    }
}
signed main(void){
    memset(hd,-1,sizeof(hd));
    scanf("%d",&n),cnt=0;
    for(int i=1,x,y;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    memset(f,inf,sizeof(f));
    dfs(1,-1);
    cout<<min(f[1][0],f[1][1])<<endl;
    return 0;
}

by >o< neighthorn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值