P2014 选课

  感觉好久没更新博客了,不知道你们有没有想我(反正也没几个人看哈哈哈)~~~

  最近接到上级指示开始写openjudge……

  今天我终于回来了……

  讲讲树形背包吧qwq。。。

  f [ x ] [ y ]表示以 x 为父亲节点,选 y 个结点的最优解是多少,每一次按照常理更新即可,对于父子关系的存储,建议开vector(比较新奇就比较喜欢,有的时候可以代替前向星,虽然我没那么写过)。。。

  我就解释一点,认为比较重要:

  看这段:

    for(int i=0;i<son[x].size();i++)
    {
        int y=son[x][i];
        dp(y);
        for(int t=m;t>=0;t--)
        for(int j=t;j>=0;j--)
        f[x][t]=max(f[x][t],f[y][j]+f[x][t-j]);
    }

  有人可能要问:不是刚刚说f [ s ] [ e ]表示以 s 为父亲节点,选 e 个结点的最优解是多少吗?那你 f [ y ] [ j ] 不是包括在 f [ x ] [ t - j ]里面嘛???

  乍一听有理……

  实际上……显然不对。。。

  我们对于来自 y 对 x 的更新,一定是在从 y 节点回溯的时候第一次遇到 x (其实也就能遇到一次),那么这个时候 f [ x ] [ ... ] 里并不包含 f [ y ] [ ... ] 的任何信息(我们不就是因为这个才要更新的嘛……),所以我们就利用刚刚更新好的最优解 f [ y ] [ ... ] 来更新它的父亲节点 f [ x ] [ ... ] 。

  还有个小细节:

  就算这个点是叶子节点,也要执行下述操作:

for(int i=m;i>=1;i--)
    f[x][i]=f[x][i-1]+s[x];

  如果有人问我边界问题,会不会一直son下去,建议你再看看我的代码,用了点小聪明(捂嘴笑)~~~

  其实还有一点比较重要(emmm……):

  因为可能不止一门课比较独立,这样就会出现森林,所以方便起见设一虚根0。

  差不多啦,放代码(去吧——皮卡丘!)

#include<cstdio>
#include<vector>
#include<iostream>
#include<cstring>
using namespace std;
vector<int> son[350];
int f[350][350],s[350],n,m;
void dp(int x)
{
    f[x][0]=0;
    for(int i=0;i<son[x].size();i++)
    {
        int y=son[x][i];
        dp(y);
        for(int t=m;t>=0;t--)
        for(int j=t;j>=0;j--)
        f[x][t]=max(f[x][t],f[y][j]+f[x][t-j]);
    }
    if(x)
    for(int i=m;i>=1;i--)
    f[x][i]=f[x][i-1]+s[x];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d%d",&x,&s[i]);
        son[x].push_back(i);
    }
    memset(f,0xcf,sizeof(f));
    dp(0);
    printf("%d",f[0][m]);
    return 0; 
} 

 

posted @ 2018-12-24 23:08  paopo  阅读(548)  评论(0编辑  收藏  举报