一句话题意:一棵树,一共n个点,每个点上有一个权值,求从1出发,走k步,最多能遍历到的权值。可以往回走。
第一(二)道树上背包题,先是看了dalao的题解,改了一点就过样例了。然而....TLE??? 改了挺久发现由于多组数据且没有“0 0”的输入,如果不在读入的时候加“~”或“EOF”就会死循环,从而导致TLE。
状态设计:设f[i][j][0/1]为以i为根的子树上,走j步,能得到的最大权值(0/1的表示会在转移方程中描述) 考虑:(此处参考dalao@zubizakeli ,侵删qwq)每个节点在最终答案中的类型:1,不经过;2,经过但不返回;3,经过且返回 (返回的定义是最终的停止节点不位于该节点的子树中) 那么可以进行转移:
之后便是一些细节问题:给数组赋初值(从0开始!走0步),更新head数组。
实现还是比较简单的啦。
code
1 #include2 #include 3 #include 4 5 using namespace std; 6 7 int n,k,tot; 8 int w[300],head[300]; 9 int f[300][200][3]; 10 struct node{ 11 int to,next,val; 12 }edge[300]; 13 14 void add(int x,int y) 15 { 16 edge[++tot].to=y; 17 edge[tot].next=head[x]; 18 head[x]=tot; 19 } 20 21 void read(int &x) 22 { 23 x=0; 24 char ch=getchar(); 25 bool flag=false; 26 while(ch<'0'||ch>'9') flag|=(ch=='-'),ch=getchar(); 27 while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 28 x=flag ? -x : x; 29 } 30 31 void TreeDp(int u,int fa) 32 { 33 for(int i=0;i<=k;i++) f[u][i][1]=w[u],f[u][i][0]=w[u]; 34 for(int i=head[u];i;i=edge[i].next) 35 { 36 int v=edge[i].to; 37 if(v==fa) continue; 38 TreeDp(v,u); 39 for(int kk=k;kk>=0;kk--) 40 for(int j=0;j<=kk;j++) 41 { 42 if(kk>=j+2) f[u][kk][0]=max(f[u][kk][0],f[v][j][0]+f[u][kk-j-2][0]); 43 if(kk>=j+2) f[u][kk][1]=max(f[u][kk][1],f[v][j][0]+f[u][kk-j-2][1]); 44 if(kk>=j+1) f[u][kk][1]=max(f[u][kk][1],f[v][j][1]+f[u][kk-j-1][0]); 45 } 46 } 47 } 48 49 void init() 50 { 51 memset(f,0,sizeof(f)); 52 memset(head,0,sizeof(head)); 53 tot=0; 54 } 55 56 int main() 57 { 58 while(scanf("%d%d",&n,&k)!=EOF) 59 { 60 for(int i=1;i<=n;i++) read(w[i]); 61 for(int i=1;i<=n-1;i++) 62 { 63 int x=0,y=0; 64 read(x),read(y); 65 add(x,y),add(y,x); 66 } 67 TreeDp(1,-1); 68 printf("%d\n",max(f[1][k][0],f[1][k][1])); 69 init(); 70 } 71 return 0; 72 }
小结:分类讨论常常也是解题重要的突破口呐。