【jzoj3418】【NOIP動態規劃專題】【選課】【樹型依賴動態規劃】
阿新 • • 發佈:2019-01-27
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;
}