经典树型DP状态压缩DP入门
leetcode常见dp题状态转移方程

Leetcode常见DP题状态转移方程一、概述动态规划(Dynamic Programming, DP)是算法设计中的一种常用方法,它通常用于优化递归算法,解决重叠子问题。
Leetcode上有许多经典动态规划问题,而理解状态转移方程是解决这些问题的关键。
本文旨在总结Leetcode常见DP题的状态转移方程,帮助读者更好地理解和掌握动态规划算法的应用。
二、背包问题1. 0-1背包问题问题描述:给定一组物品,每种物品都有重量和价值,要求在限定的总重量下,如何使得所装载的物品总价值最高。
状态转移方程:```dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]), 1 <= i <= n, 1 <= j <= C```其中,dp[i][j]表示前i个物品中最大重量不超过j时的最大价值,weight[i]表示第i个物品的重量,value[i]表示第i个物品的价值,C 表示背包的容量,n为物品的个数。
2. 完全背包问题问题描述:给定一组物品,每种物品都有重量和价值,每种物品不限数量,要求在限定的总重量下,如何使得所装载的物品总价值最高。
状态转移方程:```dp[i][j] = max(dp[i-1][j], dp[i][j-weight[i]] + value[i]), 1 <= i <= n, 1 <= j <= C```其中,dp[i][j]表示前i个物品中最大重量不超过j时的最大价值,weight[i]表示第i个物品的重量,value[i]表示第i个物品的价值,C 表示背包的容量,n为物品的个数。
3. 多重背包问题问题描述:给定一组物品,每种物品都有重量和价值,每种物品有限数量,要求在限定的总重量下,如何使得所装载的物品总价值最高。
状态转移方程:```dp[i][j] = max(dp[i-1][j], dp[i-1][j-k*weight[i]] + k*value[i]), 1 <= i <= n, 1 <= j <= C, 0 <= k <= num[i]```其中,dp[i][j]表示前i个物品中最大重量不超过j时的最大价值,weight[i]表示第i个物品的重量,value[i]表示第i个物品的价值,num[i]表示第i个物品的数量,C表示背包的容量,n为物品的个数。
2024年CSP NOIP 夏令营课程设计

2024 夏令营各营列表及说明(不分年级只分难度)务必选取本人匹配的营:a、请依照营别说明和对应课表选班;b、请进行学业水平测试选班;c、学业顾问推荐。
CSP-J 精英营
7 月 15-21 济南
系统学习过 CSP-J 知识体系,能用数据结构和算法写简单程序。但做题量不足、 难度不够、应用及变形能力 弱。
对算法知识进行梳理并通过补充大量经典例题增加难度。 课程内容覆盖 J 组的所有内容, 并还会涉及少量必要的 S 组的拓展内容。目标 200 分以上
4890
CSP-J 刷题实战 梳理营
7 月 23-30 济南
对 J 组知识已经相对熟悉, 知识储备完整, 但刷题量小、 刷题质量不高。利用知识储备分析、构造、解决问题能力不足、 实战能力弱
全模块。通过训练突破没有思路或有思路不会写的瓶颈,“快速审题+解题思路+代码实现+算法模型的构建 ”为主, 着力于分析能力、 构造能力、 解题能力的培养。目标具备 200 分以上能力
4890
S NOIP 刷题实 战梳理营
7 月 23-8 月 3 济南
对 S NOIP 知识已经相对熟悉, 知识储备完整但系统 性不强 。 缺少模块专项模拟和全真模拟 , 分析、构造、解决问题能力不足 , 实战能力弱
系统学习过 CSP-J 知识体系,能用数据结构和算法写简单 程序。但做题量不足、 难度不够、应用及变形能力弱。
对算法知识进行梳理并通过补充大量经典例题增加难度。 课程内容覆盖 J 组的所有内 容,并还会涉及少量必要的 S 组的拓展内容。目标 200 分以上
注:以上课程可能根据学生实际情况略有微调,以实际发放的课表为准。每课时 50 分钟。
DP的线段树优化(1)

UESTC1501 defend lines
• 给了一列N个数(N<=200000)现在可以删除其中连续的一段,问最 长上升子串为多少。
• 分析:很容易想到用dp[p][state]..当state =0表示以p结尾的连续串 最长上升子串为多少..state=1表示以p结尾有一段被去掉的最长上 升子串为多少 • state=0的时候很好维护..只和它前一个位置的状态有关.. • state=1的时候..分为两种情况来看..一个前面已经有一截被拿去了. 所以接下来要一直连续..这个更新和上面是一样的..而另一种是当 前这个位置前面一截是空的(其实也可以都是连续的..不影响结 果)...这里就要靠线段树来快速找比它小的最长连续上升子串为多 少 • 值得注意的是数的范围远远大于N...所以用离散化来优化空间
题意:给你n的序列求最长子序列,该序列里|a[i]-a[i1]|<=d. ( 1<=n<=100000 , 0<=d<=100000000)
• 分析:f[i]=max(f[j]+1) |a[j]-a[i]|<=d • 最佳决策点j的寻找即在区间[a[i]-d,a[i]+d]寻找f的最大值。 • 所以,可以使用线段树来进行优化。 • 以a[i]的值的范围作为线段树的覆盖区间。因为a[i]较大,n较小, 所以必须先离散化。
poj2750
给出一环形数组,数组首尾是相邻的,现在有m条指令,每条指令 会修改数组中某个数。每条指令过后,需要你求出最大子段和,最 大子段和不能包含所有元素。 4 <= N <= 100000,4 <= M <= 1,并将前n-1个元素复制到数组末 尾。现在的问题就变成了修改数组中某个数,求该数组中最大的 子段和,要求子段的长度必须小于n。 • 用DP直接求最大子段和,查询一次需要O(N),总的时间复杂度为 O(N*N)。 • 求区间内的最大子段和可以用线段树来维护,但是,在2*n-1的数 组中,长度等于n-1的区间就有n个,需要在每个区间中去查询, 问题没有得到简化。 • 到这里似乎无法继续往下思考。
DP入门(2)——DAG上的动态规划

DP⼊门(2)——DAG上的动态规划有向⽆环图(DAG,Directed Acyclic Graph)上的动态规划是学习动态规划的基础。
很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。
⼀、DAG模型【嵌套矩形问题】问题:有n个矩形,每个矩形可以⽤两个整数a、b描述,表⽰它的长和宽。
矩形X(a , b)可以嵌套在矩形Y(c , d)中当且仅当a<c,b<d,或者b<c,a<d(相当于把矩形X旋转90°)。
例如(1,5)可以嵌套在(6, 2)内,但不能嵌套在(3, 4)内。
你的任务是选出尽可能多的矩形排成⼀⾏,使得除了最后⼀个之外,每个矩形都可以嵌套在下⼀个矩形内。
如果有多解,矩形编号的字典序应尽量⼩。
分析:矩形之间的“可嵌套”关系是⼀个典型的⼆元关系(我的理解是两个矩形之间存在关系),⼆元关系可以⽤图来建模。
如果矩形X可以嵌套在矩形Y⾥,就从X到Y连⼀条有向边。
这个有向图必然是⽆环的,因为⼀个矩形⽆法直接或间接地嵌套在⾃⼰内部。
换句话说,它是⼀个DAG。
这样,所要求的便是DAG上的最长路径。
【硬币问题】问题:有n种硬币,⾯值分别为V1, V2, ..., V n,每种都有⽆限多。
给定⾮负整数S,可以选⽤多少个硬币,使得⾯值之和恰好为S?输出硬币数⽬的最⼩值和最⼤值。
1 <= n <= 100, 0 <= S <= 10000, 1 <= V i <= S。
分析:此问题尽管看上去和嵌套矩形问题很不⼀样,但本题的本质也是DAG上的路径问题。
将每种⾯值看作⼀个点,表⽰“还需要凑⾜的⾯值”,则初始状态为S,⽬标状态为0。
若当前在状态 i,每使⽤⼀个硬币 j,状态便转移到i - V j。
补充:这个模型和上⼀题类似,但也有⼀些明显地不同之处:上题并没有确定路径的起点和终点(可以把任意矩形放在第⼀个和最后⼀个),⽽本题的起点必须为S,终点必须为0。
P1433吃奶酪(洛谷)状压dp解法

P1433吃奶酪(洛⾕)状压dp解法嗯?这题竟然是个绿题。
这个题真的不(很)难,我们只是不会计算2点之间的距离,他还给出了公式,这个就有点……我们直接套公式去求出需要的值,然后普通的状压dp就可以了。
是的状压dp。
这个题的数据加强了,早已经不是搜索可以驾驭的了。
搜索的效率实在是有点低,我来算⼀个不准的效率,搜索的效率应该是O(n!)。
应该是吧。
状压dp只需要短短的O(2^n*n*n就可以了)。
状态共有2^n*n个,每次查找下⼀步需要O(n)的效率,所以状压dp的效率是O(2^n*n*n)。
状压dp⽤⼀个⼆进制数表⽰哪个奶酪曾经吃过,我们先看看这些⼆进制数的基本操作:不知道与,或,异或运算的可以百度⼀下。
第⼀种操作,查询第i个奶酪有没有吃过。
x表⽰现在奶酪的情况(2进制数)我们要判断第i个奶酪有没有被吃if((x&(1<<i-1))==0)这样写就可以啦。
其中&的意思是按位与运算,就是说在2进制下,2位同时为1,结果才为1,也就是说,如果第i个奶酪被吃掉了,x那⼀位就是1,按位与运算后的结果也是1,如果按位与运算后的结果是0,第i个奶酪就没有被吃掉。
第⼆种操作,吃掉第i个奶酪后,x会变成什么样⼦?x=x|(1<<i-1)这样写,|是按位或运算,就是在⼆进制⾥,2位有⼀个为1,结果为1,1<<i-1的第i位是1,和x进⾏按位或运算就让x的第i位也变成1了。
做这个题只需要这两个操作,剩下的操作有兴趣的同学可以去百度⼀下。
接下来就是代码了:#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<cstdio>using namespace std;double a[20],b[20],shu=999999.0;//初始化double dp[40000][20];double sz[20][20];//sz[i][j]是表⽰第i个奶酪到第j个奶酪有多远。
DP及其操作流程

DP及其操作流程DP(动态规划)是一种通过空间换时间的优化方法,适用于有重叠子问题和最优子结构性质的问题。
DP算法适用于求解最优化问题,其基本思想是将原问题划分为若干个相互重叠的子问题,逐步求解子问题,并保存每个子问题的最优解,最终得到原问题的最优解。
DP的操作流程包括以下几个步骤:1.确定状态:首先确定问题的状态,即用什么变量来表示问题的状态。
状态可以是用一个或多个变量来描述问题的特征,例如背包问题中的背包容量和物品数量。
2.定义状态转移方程:根据问题的最优子结构性质,定义状态之间的转移关系。
状态转移方程描述了子问题之间的递推关系,可以通过递归或迭代的方式求解。
3.初始化边界条件:确定初始状态的值,即将问题的边界条件定义为初始状态的值。
通常需要设置初始状态或者一些特殊的边界条件,以便开始DP算法的递推过程。
4.递推求解:根据状态转移方程和初始状态,逐步求解子问题,并保存每个子问题的最优解。
通常需要使用一个二维数组或者其他数据结构来保存子问题的解。
5.返回结果:根据子问题的最优解,得到原问题的最优解。
通常是在递推过程中保存一些状态信息,最后根据这些信息恢复原问题的最优解。
DP算法的时间复杂度通常是O(n^2)或者O(n^3),其中n是问题的规模。
通过合适地定义状态转移方程和设计递推过程,可以优化DP算法的时间复杂度,降低计算复杂度。
举个例子来说明DP算法的操作流程:假设有一个背包容量为W,有n 个物品,每个物品的重量分别为w1,w2,...,wn,价值分别为v1,v2,...,vn。
要求在背包容量不超过W的情况下,装入物品的最大总价值。
1.确定状态:定义状态dp[i][j]表示在前i个物品中,背包容量为j 时的最大总价值。
2.定状态转移方程:dp[i][j]的值可以由dp[i-1][j]和dp[i-1][j-w[i]]+v[i]决定,即在前i-1个物品的情况下,背包容量为j的最大总价值与装入第i个物品后的最大总价值之间取最大值。
树的直径的两种求法
树的直径的两种求法打多校才发现连树的直径都忘了,赶紧来复习⼀下树的直径这⾥只讲裸的树的直径..有两种常见的⽅法去求树的直径树形dp写法:第⼀种是树形dp,我们随便选个结点作为根将有根树转化为⽆根树,⽤dp[x]表⽰以x为根的⼦树中从x出发的最长链的长度,如果Yi是x的⼉⼦们,那么状态转移是这样的dp[x]=max(dp[x],dp[y]+Edge),接下来考虑经过x的最长链的长度f[x],那么max(f[i],1<=i<=n)就是树的直径了,假设yi,yj是x的两个直接相连的结点,那么f(x)=max(dp[yi]+dp[yj]+x与yi,yj的距离)具体实现就在下⾯了#include <bits/stdc++.h>using namespace std;#define de(a) cout << #a << " = " << a << endl#define rep(i, a, n) for (int i = a; i < n; i++)#define per(i, a, n) for (int i = n - 1; i >= a; i--)typedef long long ll;typedef unsigned long long ull;typedef pair<int, int> PII;typedef pair<double, double> PDD;typedef vector<int, int> VII;#define inf 0x3f3f3f3fconst ll INF = 0x3f3f3f3f3f3f3f3f;const ll MAXN = 1e3 + 7;const ll MAXM = 1e5 + 7;const ll MOD = 1e9 + 7;const double eps = 1e-6;const double pi = acos(-1.0);int n, m;struct Edge{int from, to, val;int next;} E[MAXM];int cnt = -1;int ans = -inf;int head[MAXN];int dp[MAXN]; /* dp[x]表⽰从结点x出发,往以x作为根的⼦树⾛,能够到达的最远距离 */void add(int from, int to, int val){E[++cnt].from = from;E[cnt].to = to;E[cnt].val = val;E[cnt].next = head[from];head[from] = cnt;}void init(){ans = -inf;cnt = -1;memset(head, -1, sizeof(head));memset(dp, 0, sizeof(dp));}/* 求树的直径 */void tree_dp(int u, int fa) /* 随便选个结点作为根结点把树转换成有根树 */{for (int i = head[u]; i != -1; i = E[i].next){int to = E[i].to;if (fa == to)continue;tree_dp(to, u);ans = max(ans, dp[u] + dp[to] + E[i].val);dp[u] = max(dp[u], dp[to] + E[i].val);}}int main(){init();cin >> n >> m;for (int i = 0; i < m; i++){int u, v, val;cin >> u >> v >> val;add(u, v, val); //建边add(v, u, val);}tree_dp(1, -1);cout << ans << endl;return 0;}两次dfs或者bfs:这样写还⽅便计算出直径上的具体结点。
信息学奥赛——树型动态规划的实例分析
信息学奥赛——树型动态规划的实例分析树型动态规划(Tree Dynamic Programming)是信息学奥赛中常用的一种算法思想,在解决一些与树相关的问题时非常有效。
本文将通过一个具体的实例对树型动态规划进行详细分析。
假设有一棵有根树,每个节点上都有一个非负整数权值,并且每个节点下都可能有若干个子节点。
现在要求选择一些节点,使得选中的节点的权值之和尽可能大,但是不能选择相邻的节点。
我们需要设计一个算法来解决这个问题。
首先,我们可以观察到如果一个节点被选中,那么它的子节点就不能被选中。
于是,我们可以定义一个动态规划的状态dp[i]表示以节点i为根的子树中选择节点的最大权值之和。
对于根节点,我们有两种情况:1. 根节点i被选择,那么它的子节点就不能被选择。
所以dp[i] = sum(dp[j]),其中j表示i的所有子节点。
2. 根节点i不被选择,那么它的所有子节点都可以被选择。
所以dp[i] = sum(max(dp[j], dp[k])),其中j和k分别表示i的所有孩子节点的子节点。
通过对根节点的两种状态的分析,我们可以得到一个递推关系:dp[i] = max(sum(dp[j]), sum(max(dp[k], dp[l]))),其中j表示i的所有子节点,k和l分别表示i的所有孩子节点的子节点。
接下来,我们需要设计一个合适的递归算法来计算dp[i]。
我们可以使用深度优先(DFS)的方式来处理每个节点,实现递归的过程。
具体的伪代码如下:```DFS(i):visit[i] = truefor j in i的所有子节点:if visit[j] == false:DFS(j)dp[i] += dp[j]for k in i的所有孩子节点:for l in k的所有子节点:dp[i] += max(dp[k], dp[l])```最后,我们只需要调用DFS函数以根节点为参数,可以得到整棵树的最优解。
dp状态转移方程
dp状态转移方程
DP(动态规划)状态转移方程是一种常用的算法思想,可以用来解决很多实际问题。
在DP算法中,状态转移方程是至关重要的一部分,它用来描述如何从一个状态转移到另一个状态。
DP状态转移方程通常可以用以下公式表示:
dp[i] = f(dp[i-1], dp[i-2], ..., dp[i-k])
其中,dp[i] 表示状态 i 的最优解,f 表示状态转移方程,k 表示转移的步长。
在实际问题中,状态转移方程的形式和具体实现方法都有很大的差别,需要根据具体问题进行调整和优化。
但是,通常状态转移方程都具有以下特点:
1. 最优子结构性质
即问题的最优解包含子问题的最优解。
这个特点是DP算法的核心,也是状态转移方程的基础。
2. 无后效性
即某个状态一旦确定,就不受之后决策的影响。
这个特点是DP 算法的前提条件,也是状态转移方程的基础。
3. 重叠子问题性质
即子问题之间存在重叠。
这个特点是DP算法的优势,可以通过记忆化搜索等方法进行优化。
总之,DP状态转移方程是一种非常重要的算法思想,在实际问题中发挥着巨大的作用。
熟练掌握状态转移方程的原理和应用方法,
对于提高算法竞赛和面试的能力都非常有帮助。
Codeforces1179D树形DP斜率优化
Codeforces1179D树形DP斜率优化题意:给你⼀颗树,你可以在树上添加⼀条边,问添加⼀条边之后的简单路径最多有多少条?简单路径是指路径中的点只没有重复。
思路:添加⼀条边之后,树变成了基环树。
容易发现,以基环上的点为根的⼦树的点中的简单路径没有增加。
所以,问题相当于转化为找⼀个基环,使得以基环上的点为根的⼦树Σ(i从1到n) sz[i] * (sz[i] - 1) / 2最⼩。
我们把式⼦转化⼀下变成求(sz[i]的平⽅和 - n) / 2。
相当于我们需要求sz[i]的平⽅和。
但是,我们并不知道哪个是基环,怎么求sz呢?我们发现⼀个性质:添加的边连接的两点⼀定是树中度数为1的点,否则,我们⼀定可以缩⼩平⽅和。
所以,根据这个性质,我们可以进⾏树形dp。
设dp[i]为以i为根的⼦树中,选择从i到⼦树中的某个叶⼦节点的路径为基环上的点,可以获得的最⼩的平⽅和。
dp[i] = min(dp[son] + (sz[i] - sz[son]) ^ 2)。
我们假设选择的基环是u -> lca(u, v) -> v ,假设fu为u到lca(u, v)的路径中lca(u, v)的前⾯⼀个节点,fv同理,那么平⽅和为ans = dp[fu] + dp[fv] + (n - sz[fu] - sz[fv]) ^ 2。
所以,我们在深搜的时候,找到所有孩⼦的dp值和sz,枚举是哪两个孩⼦来更新平⽅和,这样最坏情况是O(n ^ 2)的,会超时。
发现状态转移⽅程中有fu和fv的乘积项,我们可以考虑斜率优化。
把⽅程移项: dp[fv] = 2 * (n - sz[fu]) * sz[fv] + (ans - dp[fu] - 2 * n * sz[fu])。
那么相当于是以sz[fv]为横坐标,dp[fv]为纵坐标,斜率为2 * (n - sz[fu])的直线,要ans最⼩,需要截距最⼩。
我们把sz从⼩到⼤排序,⽤单调队列维护⼀个下凸包,之后在单调队列⾥⼆分即可。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
TSP
最后的结果是: min( dp[( 1<<n ) – 1][j] ) ( 0 <= j < n ); 技巧:利用2进制,使得一个整数表示一个点 集,这样集合的操作可以用位运算来实现。 例如从集合i中去掉点j: k = i & ( ~( 1 << j ) ) 或者 k = i - ( 1 << j )
习题
树型动态规划 nkoj 1791 Party at Hali-Bula nkoj 1678 Strategic game nkoj 1794 Bribing FIPA (需要2重dp) poj 1946 Rebuilding Roads (需要2重dp) 状态压缩动态规划 nkoj 1068 Islands and Bridges poj 3229 The Best Travel Design poj 1038 Bugs Integrated, Inc.
经典入门
树型动态规划和状态压缩动态规划不能允许上传同样的,所以我在 这里改改 财富值为零,请随便下载
树型动态规划
什么是树型动态规划: 树本身就是一个递归的结构,所以在树上进 行动态规划或者递推是在合适不过的事情。 必要条件:子树之间不可以相互干扰,如果 本来是相互干扰的,那么我们必须添加变量 使得他们不相互干扰。
TSP
所以一个整数i就表示了一个点集; 整数i可以表示一个点集,也可以表示是 第i个点。 状态表示:dp[i][j]表示经过点集i中的点 恰好一次,不经过其它的点,并且以j点 为终点的路径,权值和的最小值,如果这 个状态不存在,就是无穷大。
TSP
状态转移: 单点集:状态存在dp[i][j] = 0;否则无穷 大。非单点集: 状态存在 dp[i][j] = min(dp[k][s] + w[s][j]) k表示i集合中去掉了j点的集合,s遍历集 合k中的点并且dp[k][s]状态存在, 点s到 点j有边存在,w[s][j]表示边的权值。 状态不存在 dp[i][j]为无穷大。
状态压缩动态规划
状态压缩动态规划: 动态规划的状态有时候比较恶心,不容 易表示出来,需要用一些编码技术,把 状态压缩的用简单的方式表示出来。 典型方式:当需要表示一个集合有哪些 元素时,往往利用2进制用一个整数表示。
经典问题: 经典问题:TSP
一个n个点的带权的有向图,求一条路径, 使得这条路经过每个点恰好一次,并且 路径上边的权值和最小(或者最大)。 或者求一条具有这样性质的回路,这是 经典的TSP问题。 n <= 16 (重要条件,状态压缩的标志) 今天讲第一个问题的状态压缩动态规划 的解法,第2个问题大同小异。
Strategic game
题目大意: 一城堡的所有的道路形成一个n个节点的 树,如果在一个节点上放上一个士兵, 那么和这个节点相连的边就会被看守住, 问把所有边看守住最少需要放多少士兵。 Fra bibliotek型的树型动态规划
Strategic game
dproot[ i ]表示以i为根的子树,在i上放置 一个士兵,看守住整个子树需要多少士 兵。
Party at Hali-Bula
简单的染色统计是不正确的
Party at Hali-Bula
人之间的关系形成树型结构 DP, 用dp[i][0]表示不选择i点时,i点及其 子树能选出的最多人数,dp[i][1]表示选 择i点时,i点及其子树的最多人数。
Party at Hali-Bula
状态转移方程:
all[ i ]表示看守住整个以i为根的子树需要 多少士兵。
Strategic game
状态转移方程: 叶子节点:dproot[k] =1; all[k] = 0; 非叶子节点: dproot[i] = 1 + ∑all[j](j是i的儿子); all[i] = min( dproot[i], ∑dproot[j](j是i的 儿子) ); 这个题目还是比较简单的,如果把题目 中看守边变成看守相邻的点呢?留给你 来思考^_^
TSP
如何表示一个点集: 由于只有16个点,所以我们用一个整数表示 一个点集: 例如: 5 = 0000000000000101;(2进制表示) 它的第0位和第2位是1,就表示这个点集里有 2个点,分别是点0和点2。 31 = 0000000000011111; (2进制表示) 表示这个点集里有5个点,分别是0,1,2,4, 5;
Party at Hali-Bula
新加一个状态dup[i][j],表示相应的 dp[i][j]是否是唯一方案。 对于叶子结点, dup[k][0] = dup[k][1] = 1. 对于非叶子结点,
对于i的任一儿子j,若(dp[j][0] > dp[j][1] 且 dup[j][0] == 0) 或 (dp[j][0] < dp[j][1] 且 dup[j][1] == 0) 或 (dp[j][0] == dp[j][1]),则 dup[i][0] = 0 对于i的任一儿子j有dup[j][0] = 0, 则dup[i][1] =0
对于叶子节点 dp[k][0] = 0, dp[k][1] = 1 对于非叶子节点i, dp[i][0] = ∑max(dp[j][0], dp[j][1]) (j是i的儿子) dp[i][1] = 1 + ∑dp[j][0] (j是i的儿子)
最多人数即为max(dp[0][0], dp[0][1]) 如何判断最优解是否唯一?
Thank you
借用了roba的Party at Hali-Bula的ppt 感谢tju的roba! 数学科学学院 06级基础数学 描述集合论方向 汪方 id:wangfangbob 邮箱:xiaofangbob@
Party at Hali-Bula
题目大意: n个人形成一个关系树,每个节点代表一个人, 节点的根表示这个人的唯一的直接上司,只 有根没有上司。要求选取一部分人出来,使 得每2个人之间不能有直接的上下级的关系, 求最多能选多少个人出来,并且求出获得最 大人数的选人方案是否唯一。 这是一个经典的树型动态规划。