版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/82937521
这道题比较有难度。。。首先可以想到f[i][k]表示i点的子树中用了k个伐木场。但是这显然没法转移。那么我们加上一维,
f[i][j][k]表示i点的子树内用了k个伐木场并且上一个用的位置为j,而当前点可选可不选(看第二维的状态),之后用子树更新即可
合并的时候和更新的时候要特殊考虑i==j的情况。
枚举这个点其他子树内用了w个,这个子树为k-w
当不选择这个点时f[i][j][k]=min(f[to][j][k-w]+f[i][j][w])
选择这个点时为dp[i][i][k]=min(dp[to][i][k-w]+dp[i][i]w])
最后合并
f[i][j][k]+=val[i]*dis[i][j]
f[i][j][k]=min(f[i][j][k],f[i][i][k])
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot=0x3f3f3f3f,fa[205],dis[205][205],f[205][205][55],val[205];
struct node
{
int to;
int nxt;
}edge[40005];
int head[205];
int cnt=1;
void init()
{
memset(head,-1,sizeof(head));
memset(fa,-1,sizeof(fa));
}
void add(int from,int to)
{
edge[cnt].to=to;
edge[cnt].nxt=head[from];
head[from]=cnt++;
}
void dfs(int u)
{
int la=u;
for(int i=fa[u];i!=-1;i=fa[i])
{
dis[u][i]=dis[u][la]+dis[la][i];
la=i;
}
if(head[u]==-1)
{
for(int i=fa[u];i!=-1;i=fa[i])
{
f[u][i][0]=dis[u][i]*val[u];
}
return;
}
if(u)f[u][u][0]=0x3f3f3f3f;
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int to=edge[i].to;
dfs(to);
for(int j=fa[u];j!=-1;j=fa[j])
{
for(int k=m;k>=0;k--)
{
int tmp=0x3f3f3f3f;
for(int w=0;w<=k;w++)
{
tmp=min(tmp,f[u][j][w]+f[to][j][k-w]);
}
f[u][j][k]=tmp;
}
}
for(int k=m;k>=0;k--)
{
int tmp=0x3f3f3f3f;
for(int w=(u!=0);w<=k;w++)
{
tmp=min(tmp,f[u][u][w]+f[to][u][k-w]);
}
f[u][u][k]=tmp;
}
}
for(int i=fa[u];i!=-1;i=fa[i])
{
for(int j=0;j<=m;j++)
{
f[u][i][j]+=val[u]*dis[u][i];
f[u][i][j]=min(f[u][i][j],f[u][u][j]);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
int a;
scanf("%d%d",&fa[i],&a);
add(fa[i],i);
dis[i][fa[i]]=a;
}
dfs(0);
printf("%d",f[0][0][m]);
return 0;
}