http://poj.org/problem?id=2486
(1)本题属于只是交叉的题目,核心思想在于递归(状态转移方程的写出),假如要求得 p 点的信息,则可以通过其所有的相邻的点 now 的信息来更新。
而 now 的信息可以由未访问且与它相邻的点的信息来更新。
(2)由于是树状结构,访问时可能会折回某一个点,然后去另一条支路上,所以走 K 步有返回起点和不返回起点两种形式。
(3)最核心的算法本质:
dp[0][g][i+j+2]=max(dp[0][g][i+j+2], dp[0][g][i]+dp[0][now][j]); dp[1][g][i+j+1]=max(dp[1][g][i+j+1], dp[0][g][i]+dp[1][now][j]); dp[1][g][i+j+2]=max(dp[1][g][i+j+2], dp[1][g][i]+dp[0][now][j]);
这里,0表示返回起点,1表示不返回;g 是所要研究的点,now是它相邻的点钟未访问的点。以上三行代码的目的在于通过now来优化g。(建议画出
草图分析+1、+2的含义)
(4)本题用到了邻接表,以下两个代码思路一致,邻接表表示有一点点的不同,想不通为什么后者WA。。。
具体代码:


#include<stdio.h> #include<vector> #include<algorithm> using namespace std; int dp[2][101][205]; int n, k; bool p[101]; vector<int>eg[101]; void DP(int g) { for(int i=0;i<=k;++i) dp[0][g][i]=dp[1][g][i]=dp[0][g][0]; p[g]=1; for(int t=0;t<eg[g].size();t++) if(!p[eg[g][t]]) { int now=eg[g][t]; DP(now); for(int i=k-1;i>=0;i--) for(int j=k-i;j>=0;j--) { dp[0][g][i+j+2]=max(dp[0][g][i+j+2], dp[0][g][i]+dp[0][now][j]); dp[1][g][i+j+1]=max(dp[1][g][i+j+1], dp[0][g][i]+dp[1][now][j]); dp[1][g][i+j+2]=max(dp[1][g][i+j+2], dp[1][g][i]+dp[0][now][j]); } } } int main() { while(scanf("%d%d", &n, &k)!=EOF) { memset(dp, 0, sizeof(dp)); memset(p, 0, sizeof(p)); for(int i=1;i<=n;++i) { eg[i].clear(); scanf("%d", &dp[0][i][0]); } for(int i=1;i<n;i++) { int x, y; scanf("%d%d", &x, &y); eg[x].push_back(y); eg[y].push_back(x); } DP(1); printf("%d\n", dp[1][1][k]); } return 0; }
未通过的代码(自制的邻接表使用有问题):


#include<stdio.h> #include<algorithm> using namespace std; const int maxn=120; int n, k; int dp[2][maxn][maxn]; bool p[maxn]; struct node { int now, next; }edge[maxn*maxn]; int tot, head[maxn]; void add_edge(int x, int y) { edge[tot].now=y; edge[tot].next=head[x]; head[x]=tot++; } void DP(int g) { for(int i=0;i<=k;++i) dp[0][g][i]=dp[1][g][i]=dp[0][g][0]; p[g]=1; for(int t=head[g];t!=-1;t=edge[t].next) if(!p[edge[t].now]) { int now=edge[t].now; DP(now); for(int i=k-1;i>=0;--i) for(int j=k-i;j>=0;--j) { dp[0][g][i+j+2]=max(dp[0][g][i+j+2],dp[0][g][i]+dp[0][now][j]); dp[1][g][i+j+1]=max(dp[1][g][i+j+1],dp[0][g][i]+dp[1][now][j]); dp[1][g][i+j+2]=max(dp[1][g][i+j+2],dp[1][g][i]+dp[0][now][j]); } } } int main() { while(scanf("%d%d", &n, &k)!=EOF) { memset(dp, 0, sizeof(dp)); memset(p, 0, sizeof(p)); for(int i=1;i<=n;i++) scanf("%d", &dp[0][i][0]); tot=0; memset(head, -1, sizeof(head)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d", &x, &y); add_edge(x, y); add_edge(y, x); } DP(1); printf("%d\n", dp[1][1][k]); } return 0; }