1. 程式人生 > >【jzoj3418】【NOIP動態規劃專題】【選課】【樹型依賴動態規劃】

【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; }