AtCoder Regular Contest 083 (upc 6611) Bichrome Tree 这辈子不可能学会的动态规划之树形DP

题目链接:https://arc083.contest.atcoder.jp/tasks/arc083_c

题目描述

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.

题目:给一棵树每个点分配一个权值和颜色,只有黑和白两种颜色,再给一个序列X,问对于一个节点u,u和他子树中颜色相同节点的权值和正好等于Xu。问你是否可以构造出这样的一棵树,保证满足u的子树中和它颜色一样的权值加起来等于Xu。

分析:我们忽略每个节点的颜色,我们考虑这样的一棵树,肯定会有一部分跟u相同,然后使u这个节点满足题意,其余的一部分节点肯定上推到u的节点来影响u,因为对于一棵树上的某点,可能你爸爸比你爷爷大(自行领悟下),所以肯定让权值和大的那个尽量给它爸爸,让那个权值小的去给它爷爷,这样使这棵树可以构造出来。我们假设u这点是黑色的话,那么肯定所有子树中黑色的权值都需要加到当前点,那么我们希望白色的尽可能小,黑色的如果很大,U点的权值肯定能弥补哪一点。

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

点u的状态有两种取值:

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

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

最后如果f[1]为无穷,说明无论如何,1号点都无法容纳与其不同色的点之和。

以上参考博客:https://blog.csdn.net/winter2121/article/details/81326928

以下是个人拙见:这个题目其余很容易就可以考虑到,可能是我笨,DP那个转移不知道是如何转移的,我也不知道他们补题是不是真的弄懂了如何转的了,下面是我自己的看法,如果不对或者不完美请多多指教。

其实就一个背包将所有的情况都表示出来,可以考虑下如果只有一个父亲有好多儿子,那么对于储存最小值的那个F[u]肯定定于对于所有的都得考虑一边。然后求出来一个最小值,对于儿子来说考虑每个儿子是作为白色还是黑色让权值更小,让那个权值小的上推到父亲,这样防止父亲爆掉,如果是不够父亲节点完全可以凑出来。

所以就是上边的那个式子 : Dp[j-X[v]]+F[v] 考虑当前不要,拿相反的,因为颜色不重要可以整体变换。 同理另外一个一样的这样的翻转。

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;
int head[maxn];
int fa[maxn];//储存父节点 
int X[maxn];//储存值 
int f[maxn];//记录另外一个颜色的权值和 
int dp[maxn];
struct node{
    int to;
    int next;
}no[maxn];
int cnt;
void add(int u,int v)//V是节点号 
{
    no[cnt]={v,head[u]};
    head[u]=cnt++;
}
void dfs(int u)
{
    for(int i=head[u];i!=-1;i=no[i].next)//先遍历所有子树  从小往上 
        dfs(no[i].to); 
    memset(dp,INF,sizeof(dp));
    dp[0]=0;
    for(int i=head[u];i!=-1;i=no[i].next)
    {
        int v=no[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[u]==0 
        f[u]=min(f[u],dp[i]);
} 
int main()
{
    int n;
    memset(f,INF,sizeof(f));
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&fa[i]);
        add(fa[i],i);
    }
    for(int i=1;i<=n;i++)//每个点的权值 
        scanf("%d",&X[i]);
    dfs(1);//搜索根点 
    if(f[1]<INF)
        printf("POSSIBLE\n");
    else
        printf("IMPOSSIBLE\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/passer__/article/details/81503859