CodeForces - 1249F(树形DP)

思路:
考虑选了一个点u就加上他的权值w[u] ,那么对于与u距离小于K的点v,作w[v]-w[u] ,相当于对v造成选u不选v的影响。如果有的点点权变成了负数,那么这个点一定不会被选。如果有的点点权还是正数且之前被w[u]更新了,那就再加上这个点的权值进行“补差”,就相当于选了这个点而没有选u,而如果没有w[u]被更新,则相当于选了 u、v 两个点

然后就是选点顺序的问题,一种可行的方案就是从叶子节点开始,因为叶子节点没有儿子了,而叶子结点对内能影响到的点,父亲也能影响到,从外面一层一层向内收,外面的肯定是处理完了的(子树w[] 为非正),那么对于当前点来说就等同于“叶子”了,所以避免了后效性。

如果从根开始选的的话,比如A 是根,设w[C]0,所以会加上 w[B],这是就相当于取了点B,而如果B和C距离大于K了,那么对于一开始选了B来说,C是可选的,而C却在之前已经抛弃了,并且被更新成了负,则会出现后效性;

AC代码

#include
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
const int N=2e2+5;
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
    LL x=0,t=1;
    char ch=getchar();
    while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return x*t;
}
struct edge
{
    int from,to,next;
    edge(){}
    edge(int ff,int tt,int nn)
    {
        from=ff; to=tt; next=nn;
    }
};

edge e[N<<1];
vector<int> a[N];
int f[N],head[N],w[N],tot,n,k;
void add(int from,int to)
{
    e[++tot]=edge(from,to,head[from]);
    head[from]=tot;
}
void dfs(int u,int pre,int deep)
{
    a[deep].pb(u);
    for(int i=head[u];i;i=e[i].next)
        if(e[i].to!=pre) dfs(e[i].to,u,deep+1);
}
void dfs2(int u,int pre,int step,int val)
{
    w[u]-=val;
    if(step==k) return;
    for(int i=head[u];i;i=e[i].next)
        if(e[i].to!=pre) dfs2(e[i].to,u,step+1,val);
}
int main()
{
    n=read(),k=read();
    for(int i=1;i<=n;i++) w[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y); add(y,x);
    }
    dfs(1,0,1);
    int ans=0;
    for(int i=n;i;i--)
    {
        for(int j=0;j<a[i].size();j++)
        {
            //printf("u=%d deep=%d\n",a[i][j],i);
            int u=a[i][j];
            if(w[u]<=0) continue;
            ans+=w[u];
            dfs2(u,0,0,w[u]);
        }
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(贪心,动态规划,ACM)