• 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)

  • 相关阅读:
    sys.exc_info()方法:获取异常信息
    tempfile模块:生成临时文件和临时目录
    fnmatch模块:用于文件名的匹配
    pathlib模块用法详解
    linecache模块 随机读取文件指定行
    fileinput模块:逐行读取多个文件
    asyncio异步IO--协程(Coroutine)与任务(Task)详解
    Python中协程异步IO(asyncio)详解
    删除某个时间段之前的文件
    Mac入门--如何使用brew安装多个PHP版本
  • 原文地址:https://www.cnblogs.com/djfuuxjz/p/11831742.html
Copyright © 2020-2023  润新知