标签:
一棵n个节点的树,树的边有正整数权,表示两个节点之间的距离.你的任务是回答这样的询问:从跟节点出发,走不超过x单位的距离,
最多能经过多少节点?同一个节点经过多次, 只能算一个.
这题同样是多天前看的, 在今天才想出解法的. 动态规划就是这么有意思 :)
遍历n个节点, 有两种情况, 第一种是遍历完之后不回到出发点, 第二种是要回到出发点.
两种都可能会重复经过某些边, 但是显然还是第二种遍历的花费会更大
在这一题中, 遍历之后不需要回到出发点.
f(i, j, 0): 表示遍历子树i的j个节点,回到i点的最少花费
f(i, j, 1): 表示遍历子树i的j个节点, 不回到i点的最少花费
莫名其妙的WA了。。。不过思路还是对的啊。。。
转移方程啊。。。。DP真是个神奇的东西啊。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<climits>
using namespace std;
vector<int> e[510],w[510];
int dp[510][510][2],n,q,siz[510];
void dfs(int u,int father)
{
dp[u][1][1]=dp[u][1][0]=0;
siz[u]=1;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
int val=w[u][i];
if(v==father)
continue;
dfs(v,u);
siz[u]+=siz[v];
for(int j=siz[u];j>1;j--)
{
for(int k=1;k<=j&&k<=siz[v];k++)
{
dp[u][j][0]=min(dp[u][j][0],dp[u][j-k][0]+dp[v][k][0]+2*val);
dp[u][j][1]=min(dp[u][j][1],dp[u][j-k][0]+dp[v][k][1]+val);
dp[u][j][1]=min(dp[u][j][1],dp[u][j-k][1]+dp[v][k][0]+2*val);
}
}
}
}
int main()
{
int cas=1;
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int x;
for(int i=0;i<=n;i++)
e[i].clear(),w[i].clear(),siz[i]=0;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=INT_MAX;
}
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[x].push_back(y);
e[y].push_back(x);
w[x].push_back(z);
w[y].push_back(z);
}
dfs(0,-1);
scanf("%d",&q);
printf("Case %d:\n",cas++);
while(q--)
{
int ans=0;
scanf("%d",&x);
for(int i=1;i<=n;i++)
{
if(dp[0][i][1]<=x||dp[0][i][0]<=x)
{
ans=i;
}
else
break;
}
printf("%d\n",ans);
}
}
return 0;
}
标签:
原文地址:http://www.cnblogs.com/water-full/p/4534758.html