【jzoj3418】【NOIP动态规划专题】【选课】【树型依赖动态规划】

description

大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课,并通过考核就能获得相应的学分。学生最后的学分是他各门课学分的总和。每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
例如,《剥皮术》就必须在选修了《屠龙术》后才能选修。
我们称《屠龙术》是《剥皮术》的先修课。
每门课的直接先修课最多之有一门。两门课也可能存在相同的先修课。
每门课都有一个课号,课号依次是1,2,3……。以下表为例说明。
课号 先修课号 学分
1 无 1
2 1 1
3 2 3
4 无 3
5 2 4
上表中1是2的先修课,即如果要选修2,则1必须已被选过。
同样,要选修3,那么1和2都一定被选修过。
每个学生可选的课程总数是一定的,请找出一种方案,使得到的总学分最多。

solution

考虑动态规划,按dfs顺序dp,设f[i][j]表示到dfs序为i的点,选了j个点,当前点可选可不选的最大贡献,每次可以转移到dfs序+1的点,也可以转移到dfs序+size的点,记录一下当前状态由哪个状态转移过来即可。

code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define ULL unsigned int
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=1000+9,inf=1e9+7;
int n,m,w[mn],a[mn],b[mn],size[mn],gra,begin[mn],to[mn],next[mn],f[mn][mn],
    g[mn][mn],h[mn][mn];
void insert(int u,int v){
    to[++gra]=v;
    next[gra]=begin[u];
    begin[u]=gra;
}
void dfs(int p){
    b[++b[0]]=p;
    size[p]=1;
    fr(i,p){
        dfs(to[i]);
        size[p]+=size[to[i]];
    }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        int fa;
        scanf("%d%d",&fa,&w[i]);
        if(fa)insert(fa,i);
        else a[++a[0]]=i;
    }
    fo(i,1,a[0])dfs(a[i]);
    fo(i,0,n)fo(j,0,m+1)f[i][j]=-inf;
    f[1][0]=0;
    fo(i,1,n)fo(j,0,m){
        int p=b[i];
        if(f[i+1][j+1]<f[i][j]+w[p]){
            f[i+1][j+1]=f[i][j]+w[p];
            g[i+1][j+1]=i;
            h[i+1][j+1]=j;
        }
        if(f[i+size[p]][j]<f[i][j]){
            f[i+size[p]][j]=f[i][j];
            g[i+size[p]][j]=i;
            h[i+size[p]][j]=j;
        }
        if(f[i+size[p]][j+1]<f[i][j]+w[p]){
            f[i+size[p]][j+1]=f[i][j]+w[p];
            g[i+size[p]][j+1]=i;
            h[i+size[p]][j+1]=j;
        }
    }
    printf("%d\n",f[n+1][m]);
    if(m>=100)return 0;
    a[0]=0;
    int x=n+1,y=m;
    while(g[x][y]){
        int nx=g[x][y],ny=h[x][y];
        if(y!=ny)a[++a[0]]=b[nx];
        x=nx;y=ny;
    }
    sort(a+1,a+a[0]+1);
    fo(i,1,a[0])printf("%d\n",a[i]);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值