http://acm.hdu.edu.cn/showproblem.php?pid=3672
$f_{i,j}$表示从$i$出发经过$j$个不同的点所要经过的最少路程
然后发现无法轻松地转移
于是设$f_{i,j,0/1}$表示从$i$出发经过$j$个不同的点,最终是否回到$i$点的最小路程
$f_{i,j,1}$的转移很好写,但要注意$f_{i,j,0}$的转移有两种不同情况
一是走入当前这棵子树不回来,二是从当前子树回来,走入一棵之前的子树不会来
#include<bits/stdc++.h> using namespace std; const int N=505; int n,rt,sz[N]; int head[N],ver[N<<1],nxt[N<<1],val[N<<1],ce; long long f[N][N][2]; bool fa[N]; void dfs(int now,int lst) { int tmp=1; f[now][1][1]=f[now][1][0]=0; for(int i=head[now];i;i=nxt[i]) { int y=ver[i]; if(y==lst) continue; dfs(y,now); tmp+=sz[y]; for(int j=tmp;j>=1;--j) for(int k=1;k<=sz[y] && k<j;++k) { f[now][j][0]=min(f[now][j][0],min(f[y][k][1]+f[now][j-k][0]+2*val[i], f[y][k][0]+f[now][j-k][1]+val[i])); f[now][j][1]=min(f[now][j][1],f[y][k][1]+f[now][j-k][1]+val[i]*2); } } sz[now]=tmp; } void adde(int a,int b,int c) { ver[++ce]=b,val[ce]=c,nxt[ce]=head[a],head[a]=ce; } int main() { int T=0; while(233) { scanf("%d",&n); if(!n) break; memset(head,0,sizeof(head));ce=0; memset(fa,0,sizeof(fa)); memset(f,0x3f,sizeof(f)); for(int i=1;i<n;++i) { int u,v,w; scanf("%d%d%d",&u,&v,&w); adde(u,v,w); adde(v,u,w); fa[u]=1; } for(int i=0;i<n;++i) if(!fa[i]) rt=i; //cout<<rt<<endl; dfs(rt,-1); int q,x; scanf("%d",&q); printf("Case %d:\n",++T); for(int i=1;i<=q;++i) { int x; scanf("%d",&x); for(int j=1;j<=n+1;++j) if(f[rt][j][0]>x && f[rt][j][1]>x) {printf("%d\n",j-1); break;} } } return 0; }