zoukankan      html  css  js  c++  java
  • [vijos1144]小胖守皇宫<树形dp>

    题目链接:https://vijos.org/p/1144

    woc我竟然A了,这道经典的树形dp或者说是树形dp的入门题我终于过了,虽然之前做过一些树形dp的题,但是这题开始还是一脸懵逼,dp方程如何定义都知道,但是不懂转移啊,这就有点伤了。。

    dp方程定义dp[i][1]节点i 选自己

         dp[i][2]节点i选自己的儿子==不选自己和父亲

            dp[i][3]节点i选自己的父亲==不选自己选父亲

    然后就是转移了。。毕竟是基础题嘛,所以转移也不难

    转移的时候我们是直接递归到叶节点然后再做前面的。。所以我们不用考虑父节点的状态

    dp[i][1]选自己的时候,儿子节点就有两种方式,选儿子自己,或者选儿子的父亲dp[i][1]+=min(dp[son][1],dp[son][3]);

    dp[i][2]选儿子时 ,儿子就选或不选两个方式,但是如果一旦所有的儿子都是不选了,我们就要找一个最小的儿子树的值在最后加上,

    dp[i][2]+=min(dp[son][1],dp[son][2])如果全部选了2,就要在结尾加上dp[i][2]+=min(dp[son][1]-dp[son][2])

    至于为啥加这个,就是这道题唯一有点思考难度的地方了,因为你是要加最小的儿子选一个的值,所以找到最小,比如时第s2个儿子,之前已经加了dp[s2][2],所以最后的时候是加上dp[s2][1]-dp[s2][2],相当于把之前的那个dp[s2][2]抵消了,就不会加重复

    dp[i][3]选父亲时,因为我们是从子往父推,所以不从父亲转移,就考虑这时候的儿子节点,儿子节点来源就是儿子自己放和儿子的儿子放

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

    好吧这就是这道题的全部了,最后只需要输出根节点的选自己和选儿子方案的最小值就可,因为根没有父亲。。。

    储存这个关系的方式有两种,一种是链表,一种是多叉树转二叉树,

    两种方式的不同点在于链表是双向的,然后重新建树,默认1为根节点

    而多叉树转二叉树是以题目给的关系建树,以没有父亲的点为根

    链表:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdlib>
     6 #include<cmath>
     7 #include<queue>
     8 #define maxn 1505
     9 using namespace std;
    10 
    11 int f[maxn][4],n;
    12 struct edge{
    13     int u,v,w,nxt;
    14 }e[maxn*10];
    15 int a[maxn],head[maxn],vis[maxn],tot;
    16 
    17 void adde(int u,int v){
    18     tot++;
    19     e[tot].u=u;e[tot].v=v;
    20     e[tot].nxt=head[u];
    21     head[u]=tot;
    22 }
    23 
    24 void work(int x){
    25     f[x][1]=a[x];
    26     int s=0x3f3f3f,p=0;
    27     for(int i=head[x];i!=-1;i=e[i].nxt){
    28         int v=e[i].v;
    29         if(vis[v]==1)continue;
    30         vis[v]=1;
    31         work(v);
    32         f[x][1]+=min(f[v][1],f[v][3]);
    33         if(f[v][1]<f[v][2]){
    34             f[x][2]+=f[v][1],p=1;
    35         }else{
    36             f[x][2]+=f[v][2],s=min(s,f[v][1]-f[v][2]);
    37         }
    38         f[x][3]+=min(f[v][1],f[v][2]);
    39     }
    40     if(p==0)f[x][2]+=s;
    41 }
    42 
    43 int main(){
    44     memset(head,-1,sizeof(head));
    45     scanf("%d",&n);
    46     for(int i=1;i<=n;i++){
    47         int num,val,sum;
    48         scanf("%d%d%d",&num,&val,&sum);
    49         a[num]=val;
    50         for(int j=1;j<=sum;j++){
    51             int b;
    52             scanf("%d",&b);
    53             adde(num,b);adde(b,num);
    54         }
    55     }
    56     vis[1]=1;work(1);
    57     printf("%d",min(f[1][1],f[1][2]));
    58 }
    View Code

    多叉树转二叉树

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdlib>
     6 #include<cmath>
     7 #include<queue>
     8 #define maxn 3005
     9 using namespace std;
    10 
    11 int n,m,a[maxn],root;
    12 int f[maxn][5],vis[maxn];
    13 int lson[maxn],rson[maxn],fa[maxn];
    14 
    15 void work(int x)
    16 {
    17     f[x][1]=a[x];
    18     int s=0x3f3f3f,p=0;
    19     for(int i=lson[x];i!=0;i=rson[i]){
    20         work(i);
    21         if(f[i][2]<f[i][1]){
    22             f[x][2]+=f[i][2];s=min(f[i][1]-f[i][2],s);
    23         }else f[x][2]+=f[i][1],p=1;
    24         f[x][1]+=min(f[i][3],f[i][1]);
    25         f[x][3]+=min(f[i][1],f[i][2]);
    26     }
    27     if(p==0)f[x][2]+=s;
    28 }
    29 
    30 int main()
    31 {
    32     scanf("%d",&n);
    33     for(int i=1;i<=n;i++){
    34         int num,val,q;
    35         scanf("%d%d%d",&num,&val,&q);
    36         a[num]=val;
    37         for(int j=1;j<=q;j++){
    38             int s,now=lson[num];
    39             scanf("%d",&s);
    40             fa[s]=num;
    41             if(j==1)lson[num]=s;
    42             else {
    43                 while(rson[now]!=0){
    44                     now=rson[now];
    45                 }
    46                 rson[now]=s;
    47             }
    48         }
    49     }
    50     for(int i=1;i<=n;i++)
    51      if(fa[i]==0)work(i),root=i;
    52     printf("%d",min(f[root][1],f[root][2]));
    53 }
    View Code

    提醒一点,多叉树转二叉树需要在执行dp之前跑个O(n)找到根节点

  • 相关阅读:
    HDU4474 Yet Another Multiple Problem BFS搜索
    HDU4473 Exam 数学分析
    2013ACM多校联合(4)
    POJ1273 网络流...
    HDU4472 Count 递推
    POJ1149 PIGS 网络流
    UVA10881 Piotr's Ants 想法题
    javascript js string.Format()收集
    修改 设置 vs.net 网站 调试 设为 起始页
    【转】HTML5杂谈 概念与现行游戏 割绳子 宝石迷阵
  • 原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7665845.html
Copyright © 2011-2022 走看看