第一道树上背包题qaq
考虑每个节点在最终答案中的类型:1,不经过;2,经过但不返回;3,经过且返回 (返回的定义是最终的停止节点不位于该节点的子树中)
对于第一种类型的节点,不用考虑。
对于后两种类型,我们设f[i][j][0]表示在第i个节点的子树中走j步且最终回到i节点所能够获得的最大权值,f[i][j][1]表示最终不回到i节点获得的最大权值。
对于一个节点的每一个子节点,都要选择其中的一种状态。我们可以把该问题抽象成分组背包问题:一个容量为j的背包,有siz组物品(siz相当于子节点数目),每组物品只能选一种。那么我们可以得到状态转移方程:
f[i][j][0] = max{f[v][k][0]+f[i][j-k-2][0]} (既然要回到该节点,那么对于子节点v来说肯定也要回到v节点,且下去上来各一步,故多用掉两步)
f[i][j][1] = max{f[v][k][0]+f[i][j-k-2][1]} (对于v节点来说,下去不回来,那么其他的节点下去后就必须回来)
f[i][j][1] = max{f[v][k][1]+f[i][j-k-1][0]} (该节点回来,那么其他节点可以不回来)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define LL long long 5 #define RI register int 6 using namespace std; 7 const int INF = 0x7ffffff ; 8 const int N = 100 + 10 ; 9 const int K = 200 + 10 ;10 11 inline int read() {12 int k = 0 , f = 1 ; char c = getchar() ;13 for( ; !isdigit(c) ; c = getchar())14 if(c == ‘-‘) f = -1 ;15 for( ; isdigit(c) ; c = getchar())16 k = k*10 + c-‘0‘ ;17 return k*f ;18 }19 20 struct Edge {21 int to, next ;22 }e[N<<1] ;23 int n, k, cnt = 0 ; int head[N], w[N], f[N][K][2] ; // 0表示回来,1表示不回来 24 inline void add_edge(int x,int y) {25 e[++cnt].to = y, e[cnt].next = head[x], head[x] = cnt ;26 }27 28 void dfs(int x,int fa) {29 for(int j=0;j<=k;j++) f[x][j][0] = f[x][j][1] = w[x] ;30 for(int i=head[x];i;i=e[i].next) {31 int y = e[i].to ; if(y == fa) continue ;32 dfs(y,x) ;33 for(int j=k;j>=0;j--) {34 for(int kk=0;kk<=j;kk++) {35 if(j >= kk+2) f[x][j][0] = max(f[x][j][0],f[x][j-kk-2][0]+f[y][kk][0]) ;36 if(j >= kk+2) f[x][j][1] = max(f[x][j][1],f[x][j-kk-2][1]+f[y][kk][0]) ;37 if(j >= kk+1) f[x][j][1] = max(f[x][j][1],f[x][j-kk-1][0]+f[y][kk][1]) ;38 }39 }40 }41 }42 43 int main() {44 while(~scanf("%d %d",&n,&k)) {45 cnt = 0 ; memset(head,0,sizeof(head)) ;46 for(int i=1;i<=n;i++) w[i] = read() ;47 for(int i=1;i<n;i++) {48 int x = read(), y = read() ;49 add_edge(x,y) ; add_edge(y,x) ;50 }51 dfs(1,0) ;52 printf("%d\n",max(f[1][k][0],f[1][k][1])) ;53 }54 return 0 ;55 }
第一次就错在了子节点中只能有一个下去不回来qwq