开始切树形dp。。。
思路:
经研究发现,当m大于3的时候,可以把m当成3来运算。
先把多叉树转化成二叉树,方便寻找每个节点的儿子和兄弟。
dp[i][j][k]: 以i节点为根节点的子树,有j个大头,节点i的状态为k,这时候的难受值。(k=0,1)当k=0时,代表小头吃这个节点,反之,大头吃。
map[i][j]: 节点i到节点j的边长。
dp[x][j][1]=min(dp[x][j][1],dp[y][k][1]+tmp[j-k][1]+map[x][y],dp[y][k][0]+tmp[j-k][1]);
dp[x][j][0]=min(dp[x][j][0],dp[y][k][0]+tmp[j-k][0]+t ,dp[y][k][1]+tmp[j-k][0]);
tmp[i][j]: 在运算一个节点之前,已经运算过的节点形成的dp[x][i][j];
t代表当一条边两边都是0时,所应该增加的难受值。
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; struct list { int l; int r; }node[1001]; int n,m,need; int map[301][301]; int min3(int a,int b,int c) { if(a>b)a=b; if(a>c)a=c; return a; } void init() { int i,a,b,c; cin>>n>>m>>need; for(i=1;i<=n;i++) { node[i].l=node[i].r=0; } for(i=0;i<n-1;i++) { cin>>a>>b>>c; if(a>b)swap(a,b); map[a][b]=c; if(node[a].l==0) { node[a].l=b; } else { a=node[a].l; while(node[a].r) { a=node[a].r; } node[a].r=b; } } } int sum[301]; int dp[301][301][10]; int tmp[301][10]; void dfs(int x) { dp[x][1][1]=0; dp[x][0][0]=0; int j,k; sum[x]=1; int i; i=node[x].l; while(i) { int y=i; dfs(y); int t; t=0; if(m==2)t=map[x][y]; sum[x]+=sum[y]; for(j=0;j<301;j++) { tmp[j][0]=dp[x][j][0]; tmp[j][1]=dp[x][j][1]; } memset(dp[x],0x2f,sizeof(dp[x])); for(j=sum[x];j>=0;j--) { for(k=j-1;k>=0;k--)dp[x][j][1]=min3(dp[x][j][1],dp[y][k][0]+tmp[j-k][1],dp[y][k][1]+tmp[j-k][1]+map[x][y]); for(k=j;k>=0;k--) dp[x][j][0]=min3(dp[x][j][0],dp[y][k][1]+tmp[j-k][0],dp[y][k][0]+tmp[j-k][0]+t); } i=node[i].r; } } int main() { init(); if(n<m+need-1) { printf("-1\n"); return 0; } memset(dp,0x2f,sizeof(dp)); dfs(1); cout<<dp[1][need][1]; return 0; }