Bichrome Tree(这辈子不可能学会的动态规划之树形DP)

                                       Bichrome Tree

                                                           时间限制: 1 Sec  内存限制: 128 MB
                                                                           提交: 270  解决: 75
                                                            [提交] [状态] [讨论版] [命题人:admin]

题目描述

We have a tree with N vertices. Vertex 1 is the root of the tree, and the parent of Vertex i (2≤i≤N) is Vertex Pi.
To each vertex in the tree, Snuke will allocate a color, either black or white, and a non-negative integer weight.
Snuke has a favorite integer sequence, X1,X2,…,XN, so he wants to allocate colors and weights so that the following condition is satisfied for all v.
The total weight of the vertices with the same color as v among the vertices contained in the subtree whose root is v, is Xv.
Here, the subtree whose root is v is the tree consisting of Vertex v and all of its descendants.
Determine whether it is possible to allocate colors and weights in this way.

Constraints
1≤N≤1 000
1≤Pi≤i−1
0≤Xi≤5 000

输入

Input is given from Standard Input in the following format:
N
P2 P3 … PN
X1 X2 … XN

输出

If it is possible to allocate colors and weights to the vertices so that the condition is satisfied, print POSSIBLE; otherwise, print IMPOSSIBLE.

样例输入

3
1 1
4 3 2

样例输出

POSSIBLE

提示

For example, the following allocation satisfies the condition:
Set the color of Vertex 1 to white and its weight to 2.
Set the color of Vertex 2 to black and its weight to 3.
Set the color of Vertex 3 to white and its weight to 2.
There are also other possible allocations.

                                                                                      [提交] [状态]

题意:

给定含n个点的树的形态,和n个数字Xv,要求给每个点赋予黑色或白色和权值,满足对于每个点v,子树v中和v同色的点的权值和等于Xv。

题解

首先每个点的权值可以任意大(因为是自己给出的),那么v的子树(不含v的部分)权值多少就无所谓了(因为缺的可以由v来补足),但是太大的话超过Xv就不可行了。

以当前节点为黑色举例,那么它的子树中黑色节点权值之和就是Xv,设f[v]为v子树中与v节点颜色相反的节点权值之和,前面假设v为黑色了,那么f[v]则代表v子树中白色节点权值之和。我们要使得f[v]尽量小

f[v]就是v的子树中 与v 不同色的结点之和,我们让这个值尽量小,就能最大可能的满足u的祖先的需求。

对每一个点v做所有孩子的背包就好。

假设u结点为黑色。讨论它的子树v。

若u,v同色那么,v的权值和即Xv要计算入u中看是否超过Xu。

若u,v不同色,f[v]的值要计算入u中看是否超过Xu。

u与v同色,把Xv装进u结点, dp[j] = min( dp[j-X[v]]+f[v] ); (放入Xv 需要先腾出Xv的空间 然后Xv的权值为f[v])

u与不同色,把f[v]装进u结点,dp[j] = min( dp[j-f[v]]+X[v] );(同上)

PS 这个题解是参考的各位大佬的题解,和自己的一些想法,这题我一直在想状态转移这方面,我感觉这是个坑点,还有要注意

一定要先dfs所有子树,因为dp数组公用,不能一边dfs让子树用dp,否则dp数组总是被子节点更改

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <cstring>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <stdlib.h>
#include <list>
#include <map>
#include <utility>
#include <time.h>
#include <set>
#include <bitset>
#include <vector>
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ms(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define ll long long
const int maxn=1e5+5;
using namespace std;
int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
ll qpow(ll x, ll y, ll mod){ll s=1;while(y) {if(y&1)s=s*x%mod;x=x*x%mod;y>>=1;}return s;}
struct node
{
    int to;
    int next;
}p[100005];
int n;
int cnt;
int dp[100005];
int x[100005];
int f[100005];
int head[100005];
int a[100005];
int add(int u,int v)
{
    p[cnt].to=v;
    p[cnt].next=head[u];
    head[u]=cnt++;
}
int dfs(int u)
{
    for(int i=head[u];~i;i=p[i].next)//注意一定要先dfs所有子树,因为dp数组公用,不能一边dfs让子树用dp,否则dp数组总是被子节点更改
        dfs(p[i].to);
    ms(dp,INF);
    dp[0]=0;
    for(int i=head[u];~i;i=p[i].next)
    {
        int v=p[i].to;
        for(int j=x[u];j>=0;j--)
        {
            int temp=INF;
            if(j>=x[v])//u,v同色;
                temp=min(temp,dp[j-x[v]]+f[v]);
            if(j>=f[v])//u,v不同色。
                    temp=min(temp,dp[j-f[v]]+x[v]);
            dp[j]=temp;
        }
    }
    for(int i=0;i<=x[u];i++)//每次维护的是f
        f[u]=min(f[u],dp[i]);
}
int main()
{
    ms(head,-1);
    cnt=0;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(a[i],i);
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&x[i]);
    ms(f,INF);
    dfs(1);
    if(f[1]<INF)
        printf("POSSIBLE\n");
    else
        printf("IMPOSSIBLE\n");



    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41021816/article/details/81534761