P2014 [CTSC1997]选课

题意为选一门课前要看它是否有前提条件:即选了一门主课才能选 “副科”,所以可以树形背包来做。
注意是不能用分组背包来做,因为这道题附件有很多个,光是两个附件的分组背包就需要四个转移方程,在这里根本没法做。
链式前向星建树。
本身这道题的数据是一组森林,但是森林很难一起dfs所以就把所有的树根都以0为根节点建一颗大树,直接链式前向星前序遍历即可。
本题最多能选M节课
转移方程 f [ p ] [ j ] f[p][j] f[p][j]是指 f [ p ] [ ] f[以p为根节点][剩余可选课数] f[p][]
因为每门课都只能选一次。所以类似01背包,因此倒序来压缩空间,从三维压缩到二维
转移方程:
f [ p ] [ j ] = m a x ( f [ p ] [ j ] , f [ v ] [ k ] + f [ p ] [ j k ] ) f[p][j]=max(f[p][j],f[v][k]+f[p][j-k]) f[p][j]=max(f[p][j],f[v][k]+f[p][jk])
就类似01背包拿当前节点的子节点或者不拿。(子节点是必须父节点被选上的时候才可以选子节点,所以用树形背包做)
解释的应该很清楚了,代码也非常简单,有问题的话就问我

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r)/2
using namespace std;
typedef long long ll;
const ll N=1e3+7;
const ll mod=2147483647;
const double EPS=1e-6;
struct node
{
    ll u,v,pre;
}edge[N];
ll head[N],n,m,f[N][N],cnt;
inline void init()
{
    memset(head,-1,sizeof head);
    memset(f,0,sizeof f);
    cnt=0;
}
inline void add(ll u,ll v)
{
    edge[++cnt].pre=head[u];
    edge[cnt].v=v;
    head[u]=cnt;
}
inline void dfs(ll p)
{
    for(int i=head[p];~i;i=edge[i].pre)
    {
        ll v=edge[i].v;
        dfs(v);
        for(int j=m+1;j>=1;--j)
        {
            for(int k=0;k<j;++k)
            {
                f[p][j]=max(f[p][j],f[v][k]+f[p][j-k]);
            }
        }
    }
}
int main()
{
    init();
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i)
    {
        ll a,b;
        scanf("%lld%lld",&a,&b);
        f[i][1]=b;
        add(a,i);
    }
    dfs(0);
    printf("%lld\n",f[0][m+1]);
    return 0;
}

还是不懂可以问我或者看这里,超详细题解

注:如果您通过本文,有(qi)用(guai)的知识增加了,请您点个赞再离开,如果不嫌弃的话,点个关注再走吧,日更博主每天在线答疑 ! 当然,也非常欢迎您能在讨论区指出此文的不足处,作者会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧