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

来源:互联网 发布:赢富数据 编辑:程序博客网 时间:2024/05/01 18:10

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;}
原创粉丝点击