题意:
求从树的根节点出发,走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)