本文由TechFlow原创,本博文仅作为知识点学习,不会用于任何商业用途! 今天我们来看一个有趣的问题,通过这个有趣的问题,我们来了解一下在树形结构当中做动态规划的方法. 这个问题题意很简单,给定一棵树,并不一定是二叉树,树上的树枝带有权重,可以看成是长度.要求树上最长的链路的长度是多少? 比如我们随手画一棵树,可能丑了点,勿怪: 如果让我们用肉眼来看,稍微尝试一下就能找到答案,最长的路径应该是下图当中红色的这条: 但是如果让我们用算法来算,应该怎么办呢? 这道题其实有一个非常巧妙的办法,我们先…
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第15篇,也是动态规划系列的第4篇. 之前的几篇文章当中一直在聊背包问题,不知道大家有没有觉得有些腻味了.虽然经典的文章当中背包一共有九讲,但除了竞赛选手,我们能理解到单调优化就已经非常出色了.像是带有依赖的背包问题,和混合背包问题,有些剑走偏锋,所以这里不多做分享.如果大家感兴趣可以自行百度背包九讲查看,今天我们来看一个有趣的问题,通过这个有趣的问题,我们来了解一下在树形结构当中做动态规划的方法. 这个问题题意很…
前置芝士:Here 本文是基于 OI wiki 上的文章加以修改完成,感谢社区的转载支持和其他方面的支持 树形 DP,即在树上进行的 DP.由于树固有的递归性质,树形 DP 一般都是递归进行的. 基础 以下面这道题为例,介绍一下树形 DP 的一般过程. 例题 洛谷 P1352 没有上司的舞会 题目描述 某大学有 $n$ 个职员,编号为 $1 \sim N$.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一…
题目链接:https://www.luogu.com.cn/problem/P1028 题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数 \(n\) ): 先输入一个自然数 \(n(n \le 1000)\) ,然后对此自然数按照如下方法进行处理: 不作任何处理; 在它的左边加上一个自然数,但该自然数不能超过原数的一半; 加上数后,继续按此规则进行处理,直到不能再加自然数为止. 输入格式 1个自然数 \(n(n \le 1000)\) 输出格式 1个整数,表示具有该性质数的个数. 问…
阮行止 上海洛谷网络科技有限公司 讲师 intro 很有意思的问题.以往见过许多教材,对动态规划(DP)的引入属于"奉天承运,皇帝诏曰"式:不给出一点引入,见面即拿出一大堆公式吓人:学生则死啃书本,然后突然顿悟.针对入门者的教材不应该是这样的.恰好我给入门者讲过四次DP入门,迭代出了一套比较靠谱的教学方法,所以今天跑过来献丑. 现在,我们试着自己来一步步"重新发明"DP. 从一个生活问题谈起 先来看看生活中经常遇到的事吧--假设您是个土豪,身上带了足够的1.5.10…
最近接触了动态规划这个厉害的方法,还在慢慢地试着去了解这种思想,因此就在LeetCode上面找了几道比较简单的题目练了练手. 首先,动态规划是什么呢?很多人认为把它称作一种"算法",其实我认为把它称作一种"思想"更为合适:利用动态规划去解决问题,其实就是逐步递推的过程,与贪心算法不同,动态规划递推的每一步都要求是当前的最优解(这是很重要的,递推的正确性依赖的就是这一点):利用动态规划解题时,必须自己定义出来状态和状态转移方程.然而,看上去简单,做起来却非常困难,因为…
动态规划的概念对于新手来说枯燥难懂,就算看懂了,做题的时候依旧抓耳挠腮的毫无头绪,这些比较难理解的算法,还是需要根据例子来一步步学习和理解,从而熟练掌握,下面,咱们就通过一个简单的小例子来学习动态规划: 数字三角形(POJ1163) 在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大. 路径上的每一步都只能往左下或 右下走.只需要求出这个最大和即可,不必给出具体路径. 三角形的行数大于1小于等于100,数字为 0 - 99 输入格式: 5      //表示三角形的行…
题目链接:https://www.luogu.org/problemnew/show/P1002 题目还算良心,提醒了结果可能很大,确实爆了int范围, 这是一开始写的版本,用递归做的,先给地图做标记,每到一个点,这个点可以走的话,选择向下走还是向右走,但是会超时. #include <iostream> using namespace std; ][]; int M, N; ; void move(int i, int j) { if (i == N && j == M) {…
2017-08-12 18:50:13 writer:pprp 对于最基础的动态规划01背包问题,都花了我好长时间去理解: poj3624是一个最基本的01背包问题: 题意:给你N个物品,给你一个容量为M的背包 给你每个物品的重量,Wi 给你每个物品的价值,Di 求解在该容量下的物品最高价值? 分析: 状态: dp[i][j] = a 剩下i件 当前容量为j的情况下的最大价值为a 如果用 i 来枚举物品编号, 用 j 来枚举重量,那么 if ( j is from 1 to weight[i]…
我一开始是这么想的 注意这道题数组下标是从大到小推,不是一般的从小到大推 f[i]表示从最高层h到第i层所花的最短时间,答案为f[1] 那么显然 f[i] = f[j] + wait(j) + (j - 1), j > i 也就是说枚举从哪个楼层过来.取最优 wait(j)表示从第j个楼层等待电梯的最短时间. 这个算法应该是正确的,但是时间复杂度很大 有n个电梯,h层的话 枚举i和j要h * h,然后算wait要枚举电梯要n 所以是h * h * n,而题目给的n最大200,h最大10000,肯…
算法导论笔记 programming 指的是一种表格法,并非编写计算机程序 动态规划与分治方法相似,都是通过组合子问题的解来求解原问题.但是分治法将问题划分为互不相交的子问题.而动态规划是应用与子问题重叠的情况,即不同的子问题有着公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题). 动态规划通常用于求解最优化问题,这类问题通常可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值的解.我们将这样的解称之为问题的一个最优解,而不是最优解,因为最优解不唯一. 动态规划设计步骤…
Longest Ordered Subsequence Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 42914 Accepted: 18914 Description A numeric sequence of ai is ordered if a1 < a2 < - < aN. Let the subsequence of the given numeric sequence (a1, a2, -, aN) be…
转载请注明出处:http://blog.csdn.net/a1dark 分析:找规律 #include<stdio.h> int main(){ int m,n; while(scanf("%d%d",&m,&n)!=EOF){ if(m==1)printf("%d\n",n+2); else if(m==2)printf("%d\n",2*n+3); else{ int s=5; for(int i=1;i<=…
#include <iostream> #include <algorithm> #include <string.h> using namespace std; ]; int main() { int N; ,, }; while (cin >> N) { memset(dp, , sizeof(dp)); ; i <= N; i++) dp[i] = ; ; i <= ; i++) { for (int j = a[i]; j <= N…
一开始看到题目感觉很难 然后看到题解感觉这题贼简单,我好像想复杂了 就算出每一行最少的资源(完全背包+二分)然后就枚举就好了. #include<cstdio> #include<algorithm> #include<cstring> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 212345; const int MAXM =…
这道题的难点在于价值可以多. 这道题我一开始用的是前面的状态推现在的状态 实现比较麻烦,因为价值可以多,所以就设最大价值 为题目给的最大价值乘以10 #include<cstdio> #include<algorithm> #include<cstring> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 1123; const in…
这道题我一直按照往常的思路想 f[i]为前i个任务的最大空暇时间 然后想不出来怎么做-- 后来看了题解 发现这里设的状态是时间,不是任务 自己思维还是太局限了,题做得太少. 很多网上题解都反着做,那么麻烦干嘛 设f[i]为前i时间内的最大空暇时间. 这里是更新后来的状态,和以前不一样. 如果i为某个任务的开始时间,则 f[i+t-1] = max(f[i+t-1], f[i]) 也就是继承过去,取max 如果不是的话 f[i] = max(f[i], f[i-1] + 1) 加上获得的空暇时间…
这道题的难点在于,前面分组的时间会影响到后面的结果 也就是有后效性,这样是不能用dp的 所以我们要想办法取消后效性 那么,我们就可以把影响加上去,也就是当前这一组加上了s 那么就把s对后面的影响全部加上 这个做法非常巧妙. #include<cstdio> #include<algorithm> #include<cstring> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace…
这道题题目给的顺序不是固定的 所以一开始要自己排序,按照w来排序 后来只要看l就可以了 然后求最长下降子序列即可(根据那个神奇的定理,LIS模板里有提到) #include<cstdio> #include<algorithm> #include<cstring> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 1123; struc…
f[i]表示从起点到第i个车站的最小费用 f[i] = min(f[j] + dist(i, j)), j < i 动规中设置起点为0,其他为正无穷 (貌似不用开long long也可以) #include<cstdio> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; typedef long long ll; const int…
这道题和前面的分组的题有点像 就是枚举最后一组的长度. 然后组数可以在第一层循环也可以在第二层循环 我自己的话就统一一下在第一层循环吧 然后这道题题意我一直没理解清楚,浪费了很多时间,写复杂了 同时初始化的问题很重要. f[i][j]为前i格j个人分配的最大值 f[0][0] = 0,其他为负无穷 因为这道题很严格,有些状态是不存在的(比如前5格分配6个人),这个时候就要设负无穷表示不存在 这个要注意 然后一开始我走进了一个坑 一开始我加了 REP(i, 0, k + 1) f[0][i] =…
这道题写了我好久, 交上去90分,就是死活AC不了 后来发现我写的程序有根本性的错误,90分只是数据弱 #include<cstdio> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 2123; int dp[MAXN][MAXN], t[MAXN]; int f[MAXN], d[MAXN], h, n…
我一开始想的是前i个区间的最大值 显然对于当前的区间,有不选和选两种情况 如果不选的话,就继承f[i-1] 如果选的话,找离当前区间最近的区间取最优 f[i] = max(f[i-1, f[j] + a[i].v()) j为i前面区间中能取得离i最近的区间 那么显然这里涉及到f[i]的时候取的最后一个区间是什么,才能比较 那么就要额外开一个last数组来记录 最后输出f[n] 这样写很麻烦,但是我还是强行写出然后还AC了 #include<cstdio> #include<algorit…
首先可以看出排序之后,最优解肯定是每一对都相邻才是最优的 那么我们就要找构成最优解的相邻组 设f[i][j]是前i个字符,k对的最小值 如果当前这个筷子不取的话,f[i][j] = f[i-1][j] 如果取的话 f[i][j] = f[i-2][j-1] + (a[i]-a[i-1])*(a[i]-a[i-1]) 取最小值就好了. #include<cstdio> #include<algorithm> #include<cstring> #include<cm…
一开始写了一个复杂度很大的方法,然后还过了(千万记得开longlong ) #include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; typedef long long ll; const int MAXN = 20; ll f[MAXN][MAXN][MAXN]; i…
我又总结了一种动归模型-- 这道题和上一道题很类似,都是给一个序列,然后相邻的元素可以合并 然后合并后的元素可以再次合并 那么就可以用这两道题类似的方法解决 简单来说就是枚举区间,然后枚举断点 加上断点左右两边的值(按照题目,可能不是加),然后在按题目加上计算合并后总的序列的值 就这一道题而言f[i][j] = max(f[i][k] + f[k+1][j] + a[i] * a[(k+1)%n] * a[(j+1)%n]); 题目中变化的可能就是 合并后总的序列的值的计算方式 万变不离其宗 #…
经典的石子合并问题!!! 设f[i][j]为从i到j的最大值 然后我们先枚举区间大小,然后枚举起点终点来更新 f[i][j] = min(f[i][k] + f[k+1][j] + sum(i, j)); 最后f[1][n]就是答案!! #include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using na…
三维的与二维大同小异,看代码. #include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 112; char a[MAXN], b[MAXN], c[MAXN]; int f[MAXN][MAXN][MAXN], path[MAXN][…
在51nod刷到过同样的题,直接秒杀 见https://blog.csdn.net/qq_34416123/article/details/81697683 #include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 1123; char…
复制上一题总结 caioj 1069到1071 都是最长公共字序列的拓展,我总结出了一个模型,屡试不爽    (1) 字符串下标从1开始,因为0用来表示字符为空的情况,而不是第一个字符     (2)初始化问题.         一般设f[i][j]为第一个字符前i个,第二个字符前j个的最优价值          f[0][0] = 0           然后要初始化f[i][0], f[0][i]      这个时候要根据题意.          这个时候就是一个字符有,一个字符空的情况  …