LG-P2014 選課
阿新 • • 發佈:2018-12-20
P2014 選課 題目連結 題目描述 在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有N門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程a是課程b的先修課即只有學完了課程a,才能學習課程b)。一個學生要從這些課程裡選擇M門課程學習,問他能獲得的最大學分是多少?
輸入格式: 第一行有兩個整數N,M用空格隔開。(1<=N<=300,1<=M<=300)
接下來的N行,第I+1行包含兩個整數ki和si, ki表示第I門課的直接先修課,si表示第I門課的學分。若ki=0表示沒有直接先修課(1<=ki<=N, 1<=si<=20)。
輸出格式: 只有一行,選M門課程的最大得分。
輸入樣例: 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 輸出樣例: 13
題解 樹形依賴揹包。 這類問題,往往是子節點需要在選擇父節點後才能選擇。
這題就是典型。這種DP是可以做到 ,雖然這裡的 n 和 m,只有 300。
優化方案:利用 dfs 序,我們會發現每次只需要從上一個遍歷到的點,和上一棵子樹轉移過來就好了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5005,maxm=maxn;
int n,m, tot,lnk[maxn],son[maxm],nxt[maxm],f[maxn][maxn],a[maxn],s[maxn],dfn[maxn],id[maxn],ti;
void add(int x,int y)
{
son[++tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;
}
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9' ) ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void dfs(int x)
{
s[id[dfn[x]=++ti]=x]=1;
for (int i=lnk[x],y;i;i=nxt[i])
{
if (dfn[y=son[i]]) continue;
dfs(y);s[x]+=s[y];
}
}
int main()
{
freopen("class.in","r",stdin);
freopen("class.out","w",stdout);
n=rad();m=rad();ti=0;tot=0;
memset(lnk,0,sizeof lnk);
memset(f,0,sizeof f);
for (int i=1,x;i<=n;++i) x=rad(),a[i]=rad(),add(x,i);
++n;++m;
if (m>n) m=n;
dfs(0);
for (int i=n;i>=1;--i)
for (int j=1;j<=m;++j)
f[i][j]=max(f[i+1][j-1]+a[id[i]],f[i+s[id[i]]][j]);
printf("%d\n",f[1][m]);
return 0;
}