动态规划练习3

题目描述:

      lw很喜欢玩一种战略游戏,在一个地图上,有n座城堡,每座城堡都有一定的宝物,在每次游戏中lw允许攻克m个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮lw算出要获得尽量多的宝物应该攻克哪M个城堡吗?

输入格式:

      第一行两个数,分别为n和m。接下来n行,每行两个数,第一个数a为攻克这个城堡前必须攻克的城堡,若a为0则该城堡可以直接攻克,第二个数b表示财宝数。

输出格式:

      第一行一个数,即最大财宝数。

样例输入:game.in

3 2

0 1

0 2

0 3

样例输出:game.out

5

数据范围:

对于30%的数据,n,m<=100。

对于100%的数据,n,m<=200。

题解:

首先,限制条件是选择m个物品,而每个物品最多选一次,跟0-1背包的区别在于有依赖关系,那么这层依赖关系我们可以借助于一个树来解决。借助dfs,从根节点开始dfs,然后直到叶子节点,回朔的时候进行0-1背包dp。

定义状态dp[i][j]表示在节点i,从以i为根节点的子树下选择j个城市的最大价值

初始化:dp[i][j]=val[i](i节点的价值)(1 < = j < = m)

转移方程 dp[father][j] = max (dp[father][j] dp[father][k]+dp[child][j-k]);由前面的dfs可见,我们是用子节点更新父节点,用j枚举父节点选择的城市数,k枚举留给其他子节点选择城市数,那么就可以转移了

注意:此题出给很多初始节点,也就是有很多森林,我们要设置一个超级root连接子树的根节点即可。

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int f[1001][1001],n,m,a[1001];
int gi()
{
    int ans=0,f=1;
    char i=getchar();
    while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    return ans*f;
}
struct node
{
    int to,next;
}edge[10001];
int head[1001],size;
void putin(int from,int to)
{
    size++;
    edge[size].to=to;
    edge[size].next=head[from];
    head[from]=size;
}
void dfs(int r)
{
    for(int i=head[r];i!=-1;i=edge[i].next)
    {
        dfs(edge[i].to);
        for(int j=m;j>=1;j--)
            for(int k=0;k<=j;k++)
            f[r][j]=max(f[r][j],f[r][k]+f[edge[i].to][j-k]);
    }
    for(int i=m+1;i>=1;i--)f[r][i]=f[r][i-1]+a[r];
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    int i,j;
    memset(head,-1,sizeof(head));
    n=gi();m=gi();
    for(i=1;i<=n;i++)
    {
        int c=gi(),b=gi();
        a[i]=b;
        putin(c,i);
    }
    dfs(0);
    printf("%d",f[0][m+1]);
    return 0;
}

 

posted @ 2017-07-25 14:20  kakakakakaka  阅读(193)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效