LuoGu P2014选课(人生第一个树上背包)
Posted quicksilverx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LuoGu 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])$
滚动数组先咕咕咕 $emmmm$
#include<cstdio> #include<cstring> #define R register #define MAXN 310 #define MAXM 310 #ifdef QuickSilverX char __B[1<<15],*__S=__B,*__T=__B; #define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++) #endif #define max(_a,_b) ((_a<_b)?_b:_a) #define min(_a,_b) ((_a>_b)?_b:_a) int read(){ register char ch; register int aa; while(ch=getchar(),ch<‘0‘||ch>‘9‘); aa=ch-‘0‘; while(ch=getchar(),ch>=‘0‘&&ch<=‘9‘)aa=aa*10+ch-‘0‘; return aa; } using namespace std; int f[MAXN][MAXN][MAXM],num[MAXN]; int head[MAXN],point[MAXN],next[MAXN],w[MAXN]; int used = 0,n,m,ans; inline void add(int u,int v) { point[++used] = v; next[used] = head[u]; head[u] = used; } void dfs(int u) { if(!head[u]) return; for(R int k = head[u];k ; k = next[k]) { R int v = point[k]; dfs(v); num[u]++; for(int i = 0;i <= (u==0?m:m-1); i++) { f[u][num[u]][i] = f[u][num[u]-1][i]; 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]); if(!u) ans = max(ans,f[u][num[u]][i]); } } } int main() { n = read(), m = read(); for(R int i = 1; i <= n; i++) { int k = read();w[i] = read(); if(k)add(k,i); else add(0,i); } dfs(0); printf("%d",ans); return 0; }
(著名哲学家沃兹基硕德曾经说过:“ 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]) 滚动数组先咕咕咕 emmmm
#include<cstdio>
#include<cstring>
#define R register
#define MAXN 310
#define MAXM 310
#ifdef QuickSilverX
char __B[1<<15],*__S=__B,*__T=__B;
#define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++)
#endif
#define max(_a,_b) ((_a<_b)?_b:_a)
#define min(_a,_b) ((_a>_b)?_b:_a)
int read(){
register char ch; register int aa;
while(ch=getchar(),ch<‘0‘||ch>‘9‘); aa=ch-‘0‘;
while(ch=getchar(),ch>=‘0‘&&ch<=‘9‘)aa=aa*10+ch-‘0‘;
return aa;
}
using namespace std;
int f[MAXN][MAXN][MAXM],num[MAXN];
int head[MAXN],point[MAXN],next[MAXN],w[MAXN];
int used = 0,n,m,ans;
inline void add(int u,int v)
{
point[++used] = v;
next[used] = head[u];
head[u] = used;
}
void dfs(int u)
{
if(!head[u]) return;
for(R int k = head[u];k ; k = next[k])
{
R int v = point[k];
dfs(v);
num[u]++;
for(int i = 0;i <= (u==0?m:m-1); i++)
{
f[u][num[u]][i] = f[u][num[u]-1][i];
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]);
if(!u) ans = max(ans,f[u][num[u]][i]);
}
}
}
int main()
{
n = read(), m = read();
for(R int i = 1; i <= n; i++)
{
int k = read();w[i] = read();
if(k)add(k,i);
else add(0,i);
}
dfs(0);
printf("%d",ans);
return 0;
}
以上是关于LuoGu P2014选课(人生第一个树上背包)的主要内容,如果未能解决你的问题,请参考以下文章