题目:https://www.luogu.org/problemnew/show/P3354
状态中要记录一个“承诺”,只需相同承诺之间相互转移即可;
然后就是树形DP的套路了。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; int n,m,head[105],ct,siz[105],fa[105],len[105]; ll ed[105],f[105][55][105][3];//点,伐木场个数,前伐木场,是否有伐木场 ll a[105],c[105][105];//c:从i到j代价 struct N{ int to,next; ll w; N(int t=0,int n=0,ll w=0):to(t),next(n),w(w) {} }edge[100005]; void init(int cr,int cnt,ll dis,int nw) { c[cr][cnt]=dis*a[cr]; if(cnt)f[cr][0][cnt][0]=c[cr][cnt]; if(!nw) { len[cr]=cnt;return; } init(cr,cnt+1,dis+ed[nw],fa[nw]); } void dfs(int x) { siz[x]=1; for(int j=1;j<=n;j++)f[x][j][0][1]=0; for(int k=1;k<=len[x];k++)f[x][1][k][1]=0; for(int i=head[x],v;i;i=edge[i].next) { dfs(v=edge[i].to); for(int j=min(m,siz[x]+siz[v]);j>=0;j--) { for(int k=0;k<=len[x];k++) { f[x][j][k][0]+=min(f[v][0][k+1][0],f[v][0][k+1][1]); f[x][j][k][1]+=min(f[v][0][1][0],f[v][0][1][1]); for(int l=max(1,j-siz[x]);l<=j&&l<=siz[v];l++) { f[x][j][k][0]=min(f[x][j][k][0],f[x][j-l][k][0]+min(f[v][l][k+1][0],f[v][l][k+1][1])); f[x][j][k][1]=min(f[x][j][k][1],f[x][j-l][k][1]+min(f[v][l][1][0],f[v][l][1][1])); } } } siz[x]+=siz[v]; } } int main() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++) { scanf("%lld%d%lld",&a[i],&x,&ed[i]); edge[++ct]=N(i,head[x],ed[i]);head[x]=ct; fa[i]=x; } memset(f,1,sizeof f);f[0][0][0][0]=0;//! for(int i=1;i<=n;i++)init(i,0,0,i); dfs(0); printf("%lld",f[0][m][0][0]); return 0; }