

讲解:
这是一道简单的树形dp以第二组样例为例
(i代表次序,a代表攻克a,需先攻克i,w代表权值)
| i | a | w |
|---|---|---|
| 1 | 2 | 2 |
| 2 | 0 | 1 |
| 3 | 0 | 4 |
| 4 | 2 | 1 |
| 5 | 7 | 1 |
| 6 | 7 | 6 |
| 7 | 2 | 2 |
这里用vector建图

用vector确立点与点之间的关系后便为上图
网上许多代码用链式前向星,会快一点,但是vector会减少编码复杂度,这个图再加一个节点就成了一个树,所以在加一个根节点,这个根节点是虚拟的以0为根节点

可以得到转移方程dp[i][j]=max(dp[i][j],dp[i][k]+dp[x][j-k]),代表含义:以i为节点,j为攻克城堡数量,比较以i为节点攻克j个城堡的价值和以i为节点攻克k个城堡的价值+以i节点的子节点攻克j-k个城堡的价值
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 1005
using namespace std;
int dp[maxn][maxn];
int vis[maxn];
vector<int>vec[maxn];
int n,m;
void input()
{
int u,w;
for(int v=1; v<=n; v++) //n次询问
{
scanf("%d %d",&u,&w);
vec[u].push_back(v);
dp[v][1]=w;//攻破v后所获得的价值
}
}
void dfs(int x)//以x为节点向下搜索
{
vis[x]=1;
int v=x;
for(int i=0; i<vec[x].size(); i++)
{
v=vec[x][i];
if(!vis[v])
{
dfs(v);
}
for(int j=m+1; j>=2; j--) //攻破m个城堡
{
for(int k=1; k<j; k++) //取k个点
{
if(dp[v][j-k]!=-1&&dp[x][k]!=-1)
{
dp[x][j]=max(dp[x][j],dp[v][j-k]+dp[x][k]);
}
}
}
}
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
if(n==0&&m==0)
break;
memset(vis,0,sizeof(vis));
memset(dp,-1,sizeof(dp));
memset(vec,0,sizeof(vec));
for(int i=0; i<=n; i++)
dp[i][0]=0;
dp[0][1]=0;
input();
dfs(0);
printf("%d\n",dp[0][m+1]);
}
}
本文详细解析了树形动态规划的基本概念,通过具体样例展示了如何利用vector构建图结构,并给出了完整的AC代码实现。文章深入浅出地介绍了树形DP的转移方程及其在攻克城堡问题中的应用。

543

被折叠的 条评论
为什么被折叠?



