Description
Input
Output
Sample Input
3 2 0 1 0 2 0 3 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 0 0
Sample Output
5 13
0-1背包裸代码:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
状态转移方程:f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]);
m是个数,j是存几个,f[i][j]表示的是以i为根攻克j个城堡(且这j个城堡必须是它子树上的,不包括它本身),dp[i][j]表示的是是以i为根攻克j个城堡(且这j个城堡必须是它子树上的,一定它本身,ans[i]表示每个城堡的宝物,所以一定有dp[i][1]=ans[i];)。
for(int k=m;k>=0;k--)
for(int j=0;j<=k;j++)
f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]);
更新f[root][0~m]数组,然后全部更新完之后更新dp[root][0~m]。
如图所示样例2,先从root即0点访问3,3没有孩子,执行更新dp操作,因为所以叶子都满足dp[i][0~m]=ans[i],所以dp[3][0~m]都等于ans[3],以下同理。
返回到root,更新f[0][m~0]。
访问root-->2-->7-->6,访问到叶子,更新dp[6][0~m]。返回7,更新f[7][m~0],
从7-->5,更新叶子节点dp[5][0~m],
从5-->7,再次更新f[7][m~0],
从7-->2,更新dp[7][0~m],返回2节点,更新f[2][m~0],
从2-->4,更新叶子节点dp[4][0~m],
从4-->2,更新f[2][m~0],
从2-->1,更新dp[1][0~m],
从1-->2,更新f[2][m~0],
从2-->root,更新dp[2][0~m],
更新f[0][m~0],更新dp[0][0~m]。
代码:
#include <iostream>
#include <string.h>
using namespace std;
#define N 205
int n,m,edgeNum=0;
int ans[N],dp[N][N],f[N][N];
int visit[N],head[N];
struct Line
{
int v,next;
}edge[N];
void add(int u,int v)
{
edge[edgeNum].v=v;
edge[edgeNum].next=head[u];
head[u]=edgeNum++;
}
void dfs(int root)
{
visit[root]=1;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int u=edge[i].v;
if(!visit[u])
{
dfs(u);
for(int k=m;k>=0;k--)
for(int j=0;j<=k;j++)
f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]);
}
}
for(int i=1;i<=m+1;i++)
dp[root][i]=f[root][i-1]+ans[root];
}
int main()
{
int a,b;
while(cin>>n>>m)
{
if(n==m&&n==0)
break;
edgeNum=ans[0]=0;
memset(f,0,sizeof(f));
memset(dp,0,sizeof(dp));
memset(head,-1,sizeof(head));
memset(visit,0,sizeof(visit));
for(int i=1;i<=n;i++)
{
cin>>a>>b;
ans[i]=b;
add(a,i);
}
dfs(0);
cout<<dp[0][m+1]<<endl;
/*for(int i=0;i<=m+1;i++)
{for(int j=0;j<=m+1;j++)
cout<<dp[i][j]<<' ';
cout<<endl;}*/
}
return 0;
}