hihoCoder#1055 : 刷油漆 (树形DP+01背包)
题目大意:给一棵带点权的树,现在要从根节点开始选出m个连通的节点,使总权值最大。
题目分析:定义状态dp(u,m)表示在以u为根的子树从根节点开始选出m个点连通的最大总权值,则dp(u,m)=max(dp(u,m),dp(u,m-k)+dp(son,k)),其中0<=k<m。这是01背包,k应该从大往小枚举。
代码如下:
# include<iostream> # include<cstdio> # include<cmath> # include<vector> # include<list> # include<queue> # include<map> # include<set> # include<cstring> # include<algorithm> using namespace std; const int N=1000; const int INF=1000000000; const double inf=1e20; int n,m; int w[105]; vector<int>e[105]; int dp[105][105]; void dfs(int u,int fa) { fill(dp[u],dp[u]+m+1,w[u]); dp[u][0]=0; 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>=2;--j){ for(int k=j-1;k>=0;--k) dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); } } } int main() { while(~scanf("%d%d",&n,&m)) { for(int i=0;i<n;++i){ scanf("%d",w+i); e[i].clear(); } int a,b; for(int i=1;i<n;++i){ scanf("%d%d",&a,&b); --a,--b; e[a].push_back(b); e[b].push_back(a); } dfs(0,-1); printf("%d\n",dp[0][m]); } return 0; }