题目大意:一棵有根树,从根节点出发,移动距离不超过num,求最多能经历多少个点?(顶点重复经过仅算一次)
问题可以转化为“经历k个点最少移动多少距离”。
模型同hdu某次bestcoder第四题Cities。具体模型分析详见那题题解。
dp[u][k][0]:经过k个顶点不回到自身的最小移动距离
dp[u][k][1]:经过k个顶点回到自身的最小移动距离
dp[u][k][1]=min(dp[u][k][1],dp[v][j][1]+dp[u][k-j][1]+2*<u,v>.value);
dp[u][k][0]=min(min(dp[u][k][0],dp[v][j][1]+dp[u][k-j][0]+2*w),dp[v][j][0]+dp[u][k-j][1]+<u,v>.value);
对与多组询问,可以先把询问从小到大排序,然后与dp值比较,可以在线性时间内得到结果。
问题:如果起点不定能否也在此复杂度下求解?
拓展1::求包含根节点的顶点个数为k的子树(不一定包含每个节点的所有子节点)中边权和的最小值
dp[u][k]=min{ dp[u][k],dp[v][j]+<u,v>+dp[u][k-j],1<=j<k&&j<=sum[v] }
注:若子树可以不包含根节点,将上式中j<k改为j<=k即可
#include<stdio.h>
#include<string.h>
#include<iostream>
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxnode=500+10;
int n,indu[maxnode],root;
struct Edge{
int to,next,value;
}edge[maxnode<<2];
int head[maxnode],tot;
//dp[u][k][0]:经过k个顶点不回到自身的最小移动距离
//dp[u][k][1]:经过k个顶点回到自身的最小移动距离
int dp[maxnode][maxnode][2];
int sum[maxnode];//以该顶点为根的子树节点个数
int q;//询问数量
int cas;//数据个数
void add(int u,int v,int w){
edge[tot].to=v,edge[tot].next=head[u],edge[tot].value=w;
head[u]=tot++;
}
void buildtree(){//建树
mem(indu,0),mem(head,-1),tot=0;
for(int i=0;i<n-1;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
indu[a]++;
add(a,b,c),add(b,a,c);
}
}
void Findroot(){//找到树根
for(int i=0;i<n-1;i++) if(!indu[i]) { root=i; break; }
}
void dfs(int u,int fa){//求解dp[u][k][0]和dp[u][k][1] O(n^3)
sum[u]=1;
dp[u][1][0]=dp[u][1][1]=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to,w=edge[i].value;
if(v==fa) continue;
dfs(v,u);
sum[u]+=sum[v];
for(int k=sum[u];k>=1;k--){//<strong>分组背包中容量(总节点个数)必须倒序以保证每个组(每个子节点为跟的树中的节点)中仅取出一个元素</strong>
for(int j=1;j<=k&&j<=sum[v];j++){
dp[u][k][1]=min(dp[u][k][1],dp[v][j][1]+dp[u][k-j][1]+2*w);
dp[u][k][0]=min(dp[u][k][0],dp[v][j][1]+dp[u][k-j][0]+2*w);
dp[u][k][0]=min(dp[u][k][0],dp[v][j][0]+dp[u][k-j][1]+w);
}
}
}
}
void solve(){//法一:O(q*n)
scanf("%d",&q);
printf("Case %d:\n",++cas);
while(q--){
int num; scanf("%d",&num);
for(int i=n;i>=1;i--)
if(dp[root][i][1]<=num||dp[root][i][0]<=num) { printf("%d\n",i); break; }
}
}
/*void solve(){//法二:O(q*log(q)+n)
scanf("%d",&q);
for(int i=1;i<=q;i++){
query[i].id=i;
scanf("%d",&query[i].d);
}
sort(query+1,query+q+1,cmp);
int cnt=1;
for(int i=1;i<=n&&cnt<=q;i++){
int tmp=min(dp[root][i][1],dp[root][i][0]);
while(query[cnt].d<tmp){
query[cnt++].num=i-1;
}
}
for(;cnt<=q;cnt++) query[cnt].num=n;
sort(query+1,query+q+1,cmp2);
printf("Case %d:\n",++cas);
for(int i=1;i<=q;i++) printf("%d\n",query[i].num);
}*/
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF,n){
buildtree();
Findroot();
mem(dp,INF);
dfs(root,-1);
solve();
}
}