• 20191110luogu3698小Q的棋盘 | 树形背包dp | 贪心


    luogu3698小Q的棋盘

    题意:

    求从树的根节点出发,走n步能经过的最多的点的数量(可以重复走点,但是重复走的步数会记录)

    树形背包dp:

    对于从0出发,我们可以这样走:

    1、选一条岔路一直走下去

    2、选一条岔路走后回到0点,再选一条岔路走下去

    对应的dp转移:

    f[0][u][j]代表从u出发走j步不一定回到u点能到达的最大步数

    f[1][u][j]代表从u出发走j步回到u点能到达的最大步数

    f[0][u][j] = max(f[0][u][j],f[0][u][k] + f[1][too][j - k - 2])//2状态,选too的路径并走回too,再走回u,从u再选一条新路径走下去

    f[0][u][j] = max(f[0][u][j],f[1][u][k] + f[0][too][j - k - 1])//2状态,选某路径走个往返,再走回u,从u选择从too走下去(注意这个状态和上一个不一样,必须要讨论这两种情况)

    f[1][u][j] = max(f[1][u][j],f[1][u][k] + f[1][too][j - k - 2]);//1状态

    需要注意的是j一定要从大到小倒序枚举,因为我们的状态是由小到大转移的,所以为了避免重复转移状态,j从小到大枚举

    还需注意的是k需要从0开始枚举,因为我们初始化f[0][u][0]= f[1][u][0] = 1,需要利用上初始状态

    时间复杂度:O(vn^2)

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 int to[205],head[205],nxt[205],cnt;
     5 int f[2][205][205],n,v;
     6 void add(int u,int v)
     7 {
     8     to[++cnt] = v;
     9     nxt[cnt] = head[u];
    10     head[u] = cnt;
    11 }
    12 void dfs(int u,int fa)
    13 {
    14     f[0][u][0] = f[1][u][0] = 1;
    15     for(int i = head[u]; i; i = nxt[i])
    16     {
    17         int too = to[i];
    18         if(too == fa)continue;
    19         dfs(too,u);
    20         for(int j = n;j >= 0;j --)
    21         {
    22             for(int k = 0;k <= j - 1;k ++)
    23             {
    24                 if(j - k - 1 >= 0)f[0][u][j] = max(f[0][u][j],f[1][u][k] + f[0][too][j - k - 1]);
    25                 if(j - k - 2 >= 0)
    26                 {
    27                     f[1][u][j] = max(f[1][u][j],f[1][u][k] + f[1][too][j - k - 2]);
    28                     f[0][u][j] = max(f[0][u][j],f[0][u][k] + f[1][too][j - k - 2]);
    29                 }
    30                 f[0][u][j] = max(f[0][u][j],f[1][u][j]);
    31             }
    32         }
    33     }
    34     for(int i = 1;i <= n;i ++)
    35     {
    36         f[0][u][i] = max(f[0][u][i],f[0][u][i - 1]);
    37         f[1][u][i] = max(f[1][u][i],f[1][u][i - 1]);
    38 //        printf("f[0][%d][%d]=%d
    ",u,i,f[0][u][i]);
    39     }
    40 }
    41 int main()
    42 {
    43     scanf("%d%d",&v,&n);
    44     for(int i = 1,a,b; i<= v - 1; i ++)
    45     {
    46         scanf("%d%d",&a,&b);
    47         add(a,b);
    48         add(b,a);
    49     }
    50     dfs(0,0);
    51     printf("%d",f[0][0][n]);
    52     return 0;
    53 }

    贪心:

    可以证明的是我们一定会走从0出发的最长链

    因为明显最优的就是不用往回走

    当n大于最长链长度时,我们再考虑往回走的问题,每多经过一个点,需要的步数+2(一来一回),直接计算即可

    当n小于等于最长链长度时,直接输出步数+1(包含根节点)

    时间复杂度:O(v)

  • 相关阅读:
    POJ 3114 Tarjan+Dijkstra
    278. First Bad Version
    209. Minimum Size Subarray Sum
    154. Find Minimum in Rotated Sorted Array II
    153. Find Minimum in Rotated Sorted Array
    710. Random Pick with Blacklist
    767. Reorganize String
    524. Longest Word in Dictionary through Deleting
    349. Intersection of Two Arrays
    350. Intersection of Two Arrays II
  • 原文地址:https://www.cnblogs.com/djfuuxjz/p/11831742.html
Copyright © 2020-2023  润新知