從這道題來的,作法差很少:
咱們能夠將題目轉化一下:
求出原來全送到0點的費用sum,再看看多建k個伐木場最多能減小多少費用,
設\(f[i][j][k]:\)爲在i節點,最近的伐木場離i的距離爲(祖輩裏),子樹內建k個伐木場的最大能減小的費用:
咱們考慮每一個點i對伐木場x的貢獻,最大化,選取k個伐木場的值:
主方程:html
for(int v=0;v<=d;++v) for(int j=min(siz[x],k);j>=0;--j) for(int o=0;o<=min(j,siz[u]);++o) f[x][v][j]=max(f[x][v][j],f[x][v][j-o]+max(f[u][v+1][o],f[u][0][o]));
中:
\(f[x][v][j]=max(f[x][v][j],f[x][v][j-o]+max(f[u][v+1][o],f[u][0][o]))\)c++
\(f[x][v][j-o]+\)表明繼承以前子樹,spa
\(f[u][v+1][o]\)將點u對在v建伐木場的貢獻上傳一下,3d
\(f[u][0][o]\)在u點新建一個伐木場code
總代碼:htm
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=106,K=52; int n,k,cnt=0,t1,t2,t3,fa[N][N],siz[N]; int q,f[N][N][K],w[N],sum=0,sumw[N],head[N]; struct edge{int nxt,to,w;}e[N<<1]; inline void add(int u,int v,int w){e[++cnt].nxt=head[u],e[cnt].to=v,e[cnt].w=w,head[u]=cnt;} inline int read(){ int T=0,F=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); return F*T; } void dfs(int x,int ffa,int d){ int u; fa[x][0]=x,fa[x][1]=ffa,f[x][0][1]=sumw[x]*w[x],f[x][1][0]=sumw[ffa]*w[x],siz[x]=1,sum+=sumw[x]*w[x]; for(int i=2;i<=d;++i) fa[x][i]=fa[fa[x][1]][i-1],f[x][i][0]=sumw[fa[x][i]]*w[x]; for(int i=head[x];i;i=e[i].nxt){ u=e[i].to,sumw[u]=sumw[x]+e[i].w,dfs(u,x,d+1),siz[x]+=siz[u]; for(int v=0;v<=d;++v) for(int j=min(siz[x],k);j>=0;--j) for(int o=0;o<=min(j,siz[u]);++o) f[x][v][j]=max(f[x][v][j],f[x][v][j-o]+max(f[u][v+1][o],f[u][0][o])); } } int main(){ n=read()+1,k=read()+1; memset(f,0xc0,sizeof(f)); for(int i=2;i<=n;++i) t1=read(),t2=read()+1,t3=read(),add(t2,i,t3),w[i]=t1; dfs(1,0,1),q=0; for(int i=1;i<=k;++i) q=max(q,f[1][0][i]); printf("%d\n",sum-q); return 0; }