题意
一棵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; }
时间: 2024-10-12 21:50:08