P1270 【“访问”美术馆】
$\large{\text{一千个Oier程序中有一千种树形DP}}$
思路都差不多的,但是每个人都有自己的状态定义与转移
不妨定义$dp[i][j]$表示,在$i$子树内,偷$j$张画,且不考虑根到$i$父节点路径代价的最短时间
$a[i]$表示$i$与其父节点的距离
$d[i]$表示$i$到根节点的距离
不难想出转移
$\large{dp[i][j]=min\left\{dp[son1][k]+dp[son2][j-k]+dis[x]\right\}}$
统计答案
枚举每个节点选几个,如果$dp[i][j]+d[i]-a[i]<=s$,更新答案
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int size[1010],s,cnt,a[1010],v[1010],e[1010][2],dp[1010][2010],ans,d[1010]; void init(int x) { int t,to; if(scanf("%d%d",&t,&to)!=2) return; d[x]+=2*t,a[x]=2*t,dp[x][0]=0; if(to) v[x]=to; else { d[++cnt]=d[x],init(e[x][0]=cnt); d[++cnt]=d[x],init(e[x][1]=cnt); } } void dfs(int x) { if(!x) return; dfs(e[x][0]),dfs(e[x][1]); size[x]=v[x]+size[e[x][0]]+size[e[x][1]]; if(v[x]) for(int i=1;i<=v[x];i++) dp[x][i]=a[x]+dp[x][0]+5*i; else for(int i=1;i<=size[x];i++) for(int j=0;j<=i;j++) dp[x][i]=min(dp[x][i],dp[e[x][0]][j]+dp[e[x][1]][i-j]+a[x]); } int main() { scanf("%d",&s); memset(dp,0x3f,sizeof(dp)); init(++cnt); dfs(1); for(int i=1;i<=cnt;i++) for(int j=1;j<=size[i];j++) if(dp[i][j]+d[i]-a[i]<s) ans=max(ans,j); printf("%d\n",ans); return 0; }