洛谷P2014选课(人生第一个树上背包)
(著名哲学家沃兹基硕德曾经说过:“$QuickSilverX$ $is$ $a$ $BB$”)
就是课与课可能有一些优先关系
这种关系我们可以通过图论建模来解决
不难发现,若将优先选修课向当前课连边,就会生成森林(每门课只有一个选修课,也就只有一条入边)
将所有无入边(没有优先课)的结点与 $0$ 相连,形成树
不难发现这是一个树上 $DP$ 与背包。。。
树本身就是个递归的结构,我们每个结点的状态肯定是先递归处理儿子结点的状况来转移的
定义状态 $F[i][j][k]$ 表示第 $i$ 的前 $j$ 个儿子结点的子树选了 $k$ 课的答案
由于选儿子结点必须要选根结点,所以我们枚举当前儿子结点所选 $l$ 课程,有以下转移方程
$F[i][j][k] = max(F[i][j-1][k],F[i][j-1][k-l] + F[son][soncnt[son]][l-1] + w[son])$
1 #include<cstdio> 2 #include<cstring> 3 #define R register 4 #define MAXN 310 5 #define MAXM 310 6 #ifdef QuickSilverX 7 char __B[1<<15],*__S=__B,*__T=__B; 8 #define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++) 9 #endif 10 #define max(_a,_b) ((_a<_b)?_b:_a) 11 #define min(_a,_b) ((_a>_b)?_b:_a) 12 int read(){ 13 register char ch; register int aa; 14 while(ch=getchar(),ch<'0'||ch>'9'); aa=ch-'0'; 15 while(ch=getchar(),ch>='0'&&ch<='9')aa=aa*10+ch-'0'; 16 return aa; 17 } 18 using namespace std; 19 int f[MAXN][MAXN][MAXM],num[MAXN]; 20 int head[MAXN],point[MAXN],next[MAXN],w[MAXN]; 21 int used = 0,n,m,ans; 22 inline void add(int u,int v) 23 { 24 point[++used] = v; 25 next[used] = head[u]; 26 head[u] = used; 27 } 28 void dfs(int u) 29 { 30 if(!head[u]) return; 31 for(R int k = head[u];k ; k = next[k]) 32 { 33 R int v = point[k]; 34 dfs(v); 35 num[u]++; 36 for(int i = 0;i <= (u==0?m:m-1); i++) 37 { 38 f[u][num[u]][i] = f[u][num[u]-1][i]; 39 for(R int j = 1;j <= i; j++) f[u][num[u]][i] = max(f[u][num[u]][i],f[u][num[u]-1][i-j] + f[v][num[v]][j-1] + w[v]); 40 if(!u) ans = max(ans,f[u][num[u]][i]); 41 } 42 } 43 } 44 int main() 45 { 46 n = read(), m = read(); 47 for(R int i = 1; i <= n; i++) 48 { 49 int k = read();w[i] = read(); 50 if(k)add(k,i); 51 else add(0,i); 52 } 53 dfs(0); 54 printf("%d",ans); 55 return 0; 56 }