题目大意:一棵点带权有根树,根节点为1。从根节点出发,走k步,求能收集的最大权值和。
题目分析:从一个点向其某棵子树出发有三种可能的情况:
1、停留在那棵子树上;
2、再回到这个点;
3、经过这个点走向了其他分支;
定义状态dp(u,k,0/1)表示在u节点为根的子树上走k步并且不回/回到u的最大权值和。则状态转移方程为:
dp(u,k,0)=max(dp(son,j-2,1)+dp(u,k-j,0),dp(u,k-j,1)+dp(son,j-1,0))
dp(u,k,1)=max(dp(son,j-2,1)+dp(u,j-k,1))
代码如下:
# include<iostream> # include<cstdio> # include<vector> # include<cstring> # include<algorithm> using namespace std; const int N=105; int n,m; int w[N]; int dp[N][N<<1][2]; vector<int>e[N]; void init() { memset(dp,0,sizeof(dp)); for(int i=1;i<=n;++i){ e[i].clear(); scanf("%d",w+i); for(int j=0;j<=m;++j) dp[i][j][0]=dp[i][j][1]=w[i]; } int a,b; for(int i=1;i<n;++i){ scanf("%d%d",&a,&b); e[a].push_back(b); e[b].push_back(a); } } void dfs(int u,int fa) { for(int i=0;i<e[u].size();++i){ int v=e[u][i]; if(v==fa) continue; dfs(v,u); for(int j=m;j>=1;--j){ for(int k=1;k<=j;++k){ dp[u][j][0]=max(dp[u][j][0],dp[v][k-1][0]+dp[u][j-k][1]); if(k>=2){ dp[u][j][0]=max(dp[u][j][0],dp[v][k-2][1]+dp[u][j-k][0]); dp[u][j][1]=max(dp[u][j][1],dp[v][k-2][1]+dp[u][j-k][1]); } } } } } void solve() { dfs(1,-1); printf("%d\n",max(dp[1][m][0],dp[1][m][1])); } int main() { while(~scanf("%d%d",&n,&m)) { init(); solve(); } return 0; }
时间: 2024-10-08 02:25:48