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;

}

  

(著名哲学家沃兹基硕德曾经说过:“ QuickSilverXQuickSilverX isis aa BBBB ”) 就是课与课可能有一些优先关系 这种关系我们可以通过图论建模来解决 不难发现,若将优先选修课向当前课连边,就会生成森林(每门课只有一个选修课,也就只有一条入边) 将所有无入边(没有优先课)的结点与0相连,形成树 不难发现这是一个树上DP与背包。。。 树本身就是个递归的结构,我们每个结点的状态肯定是先递归处理儿子结点的状况来转移的 定义状态 F[i][j][k]F[i][j][k] 表示第 ii 的前 jj 个儿子结点的子树选了 kk 课的答案 由于选儿子结点必须要选根结点,所以我们枚举当前儿子结点所选 ll 课程,有以下转移方程 F[i][j][k] = max(F[i][j-1][k],F[i][j-1][k-l] + F[son][soncnt[son]][l-1] + w[son])F[i][j][k]=max(F[i][j−1][k],F[i][j−1][k−l]+F[son][soncnt[son]][l−1]+w[son]) 滚动数组先咕咕咕 emmmmemmmm

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

原文地址:https://www.cnblogs.com/QuickSilverX/p/10663190.html

时间: 2024-11-09 03:40:01

LuoGu P2014选课(人生第一个树上背包)的相关文章

MZOJ 1132 &amp;&amp; LuoGu P2014 选课

MZOJ 1132 && LuoGu P2014 选课    [传送门] #include<bits/stdc++.h> #define maxn 100000 #define maxm 500 using namespace std; int k=0,head[maxn]; int f[maxm][maxm],w[maxn]; int ans=0; int N,M; struct node{ int v,nxt; }e[maxn<<1]; void adde(in

Luogu P2014 选课 题解报告

题目传送门 [题目大意] 有n门选修课,每一门课都有固定的学分$S_i$,每个学生可以选m门课.有些选修课有先修课,每一门课最多只有一门先修课,求能获得的最多学分. [思路解析] 设f[x][t]表示在以x结点为根的子树中选t门课能获得的最大学分,x的子结点集合为son[x],子结点个数为p,且对于x的第i个子结点son[i],以其为根结点的子树中选课数量为$C_i$,则转移方程为:$$f[x][t]=max(\sum_{i=1}^{p}f[son[i]][c[i]])+s[i](满足\sum_

Luogu P2014 选课

题面 对于这道题,我们考虑在树形dp上套背包.我们会非常自然的采用dfs扫描整棵树,然后对树上的每个节点都进行一次背包. 计\(dp[i][j]\)为在以第\(i\)号节点为根结点的子树中,用题目中选法选取\(j\)项的最大值. 我们在dfs的过程中,采用递归的方式,在子节点都处理完之后,便考虑将所有子节点的答案综合,得到当前节点的答案. 很显然,就是在容量为\(j\)的01背包中放下\(i\)节点的所有子节点背包中的答案,我们很容易想到下面的DP方程 \[f[x][j]=max(f[to][k

Luogu P2014 选课 (树形DP)

题目 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行,第i+1行

【题解】Luogu P2014 选课

Problem 树上背包问题的典例,记下来 solution 设\(dp[x][t]\)表示以\(x\)为子树,选\(t\)门课获得的最大学分 设\(p\)是\(x\)的子节点数量,\(c_i\)是\(x\)的子节点\(y_i\)选修的课数 转移方程如下 \[dp[x][t]=max_{\sum_{i=1}^pc_i=t-1}\{\sum_{i=1}^pdp[y_i][c_i]\}+pnt[x]\] 事实上,这是一个分组背包模型,对于每个节点\(x\),每个子节点\(y_i\)是一个组,在其中选

P2014 选课 - 树形DP[树形背包DP]

P2014 选课 传送门 思路: 树形背包DP模型,\(f[i,j]\)表示以\(i\)为根的子树中,选了\(j\)门课的最大学分.树形DP常以子树\(i\)为阶段.树形背包DP相当于树上分组背包DP.\(f[u,j]=max\{f[u,j],f[u,j-k]+f[v,k]~|~v\in~son(u)\}\).我们枚举从u的子树v中选的课数k,将\(f[v,k]\)作为获得的价值加到\(f[u,j-k]\)得到\(f[u,j]\).注意到当前子树根节点u是必须选的,所以要从\(f[u,j-1]\

P2014 选课 (树上背包)

树上背包: 树形背包就是原始的树上动归+背包,一般用来处理以一棵树中选多少点为扩展的一类题,基本做法与树上dp无异,不过在状态转移方程中会用到背包的思想. 它基本上是这个样子的: 存图),然后dfs进去补全子节点的信息,f数组的意思是以fa为中转点,找出fa往下的可取1~j个点时各自的最大收益. void dfs(int fa){ for(int i=0;i<son[fa].size();i++){ int ny=son[fa][i]; dfs(ny); for(int j=m+1;j>=1;

luogu 2014 选课 树上背包

树上背包 #include<bits/stdc++.h> using namespace std; const int N=310; const int inf=0x3f3f3f3f; vector<int> son[N]; int f[N][N],s[N],n,m; void dfs(int u){ f[u][0]=0; for(int i=0;i<son[u].size();i++){ int v=son[u][i]; dfs(v); for(int j=m;j>0

树形DP 洛谷P2014 选课

P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行