DP
文章平均质量分 73
DP专栏
荼白777
这个作者很懒,什么都没留下…
展开
-
整数划分(完全背包变形&背包问题求方案)
传送门题面思路Code二维版本#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N = 1e3 + 10;const int MOD = 1e9 + 7;ll f[N][N],n;void solve(){ int n; cin >> n; for(int i=1;i<=n;++i) f[i][0]原创 2022-05-01 12:06:54 · 250 阅读 · 0 评论 -
单调队列优化DP
模型求一段区间(窗口)最值的时候,当然这个窗口不需要固定大小,只要保证首尾是递增的即可;见经典模型滑动窗口;如何使用按照常规DP思路定义好状态,写好转移方程(保证正确性)和其他优化方式一样,对转移方程做等价变换;例题最大子序和题面思路时间复杂度是O(n)O(n)O(n)的;注意一个点,子序列的长度不能为空!!因此我们滑动窗口的右边框是当前点iii往左边移动一个位置;Code#include <iostream>#include <cstring>原创 2022-04-12 20:54:50 · 1066 阅读 · 1 评论 -
Damaged Bicycle(期望 + 状压DP)
题面思路假设当前单车是now,前一个单车是pre;如果前一辆单车是好的,那么我们直接骑走;如果前一辆单车是坏的,那么我们有两种决策;1.直接摆烂,走路去学校2.寻找当前的单车(当前的单车也有好也有坏的情况,按这个思路循环即可全部求解)f(s,i)f(s,i)f(s,i)表示已经遍历过的自行车集合为sss,最后一次停留在iii点;然后按上述思路进行DP即可;因为我们只涉及3个点,起点、自行车、终点,我们不需要每个点都跑最短路,只需要在每个自行车点跑最短路即可;Code#原创 2022-03-30 12:30:24 · 872 阅读 · 0 评论 -
状压DP第二部分
题面思路前提知识:位运算枚举子集f(s)f(s)f(s)表示状态sss的人已经过河,所花费的最少时间;转移也很容易想到,枚举状态sss的子集来转移,对于多出来的部分暴力计算即可;代码中f[s] = min(f[t] + time,f[s]);其实换成f[s] = min(f[sub] + time,f[s]);也是对的;但是sub为0的情况也是合法的,因此循环的写法要注意Code#include <iostream>#include <cstring>#incl原创 2022-03-27 23:16:39 · 429 阅读 · 0 评论 -
状态压缩DP例题
引入状压DP一般分为两种,一种是基于连通性(棋盘状)的,另一种是基于集合的;例题小国王(基于连通性)题面小国王思路不难发现,假设现在是第iii行,它的国王怎么放只取决于i−1i-1i−1行;因此我们可以考虑定义状态f(i,s)f(i,s)f(i,s)表示前iii行,第iii行的状态是sss的所有方案;但是发现又有次数这个限制因此我们定义状态f(i,j,s)f(i,j,s)f(i,j,s)表示所有只摆在前i行,已经摆了jjj个国王,第iii行摆放的状态是sss的所有方案;现在考虑原创 2021-11-04 10:47:45 · 357 阅读 · 0 评论 -
状态机DP例题
引入我认为,状态机DP很常见,通常跟其他DP混合在一起;一个显著特征就是,我们需要当前点的状态;或者说,我们状态之间的转移,是与我们当前点的状态是有关系的;假设当前点为A1A_1A1,那么只能从状态B1B_1B1转移而不能从状态B2B_2B2转移;例题大盗阿福大盗阿福思路f(i,j)f(i,j)f(i,j)表示前iii个店铺,当前状态为jjj的所有抢法中的最大值;如果当前店铺要抢,那么前一个必然不能抢;即f(i,1)=f(i−1,0)+a[i]f(i,1)=f(i-1,0)原创 2021-10-31 12:32:48 · 228 阅读 · 0 评论 -
Codeforces Round #747 (Div. 2) E2 —— 树形DP + 状态机
E2题目思路这题很像皇宫看守这道题;因为我做过皇宫看守这道题,很自然就想到树形DP;我们定义f(i,j)f(i,j)f(i,j)表示以iii为根的子树,状态为jjj所拥有的方案数;当j=0j=0j=0表示当前节点的颜色为任意当j=1j=1j=1表示当前节点的颜色为黄、白;当j=2j=2j=2表示当前节点的颜色为绿、蓝;当j=3j=3j=3表示当前节点的颜色为红、橙;也就是互斥的颜色放在同一组中;我们设jjj为iii的子节点;f(i,0)∗=f(j,1)+f(j,2)f(i原创 2021-10-17 14:44:59 · 92 阅读 · 0 评论 -
皇宫看守 —— 树形DP + 状态机
题面皇宫看守思路我们定义f(i,j)f(i,j)f(i,j)表示以节点iii为根的子树,当前状态为jjj的所有方案,我们从中取最小的;jjj的取值有三个,0,1,20,1,20,1,2;f(i,0)f(i,0)f(i,0) 表示点iii的父节点有守卫(当前节点iii不放)f(i,1)f(i,1)f(i,1) 表示点iii的子节点有守卫(当前节点iii不放)f(i,2)f(i,2)f(i,2) 表示点iii有守卫现在我们考虑状态转移;因为f(i,0)f(i,0)f(i,0)是其父节点上原创 2021-10-16 23:34:59 · 110 阅读 · 0 评论 -
数字转换 —— 数论+ 树形DP
题面题目思路一个数的约数之和是固定的,而这个约数之和可能是多个数的约数和;因此如果我们约数之和 →→→ 约数连一条边,必然可以形成树的形状(并且题目说了不能出现重复数字);这样问题就转化成了求树的直径;接下来是关于约数和部分;如果我们暴力的去筛每个数的因子,时间复杂度为O(nn)O(n \sqrt{n})O(nn)我们可以用埃氏筛的思想,考虑枚举某个数能成为谁的因子;也就是枚举倍数;这样时间复杂度为O(nlogn)O(nlogn)O(nlogn)当然还有线性筛的方法O(n)O(n原创 2021-10-16 16:18:25 · 128 阅读 · 0 评论 -
两种树的直径
树的直径一般有两种说法;注意,每棵树的直径可能不唯一;第一种是树的边权总和最大的路径(有边权的树);另一种是树上经过最多边的路径(无边权的树);第一种题面题目思路权值最大的路径有两种可能;一种是一路走到黑;另一种是取一条路的最大值加另一条路的次大值对于第一种情况,我们直接往下取即可;对于第二种情况,我们相当于要取挂在某个结点上的两条路,也就是要穿过父节点;具体见注释Code#include <iostream>#include <cstdio原创 2021-10-16 15:19:46 · 143 阅读 · 0 评论 -
树的中心 —— 树型DP
题面题目思路因为要求某个点到其他结点的最远距离最近;那么我们只需要处理出每个结点的最远距离从中取一个min即可;取某个结点的最远距离可以从该结点的上方转移过来也可以从该结点的下方转移过来;首先讨论从下方转移过来(由子节点转移到父节点)因为题目要求最远的,因此我们只需要维护一个最远的距离即可;如下所示//u是当前结点,to是子节点max(dfs_d(to,u) + e[i].val)接着考虑从上方转移过来(由父节点转移到子节点)比如说我们要由结点111更新结原创 2021-10-16 14:36:51 · 148 阅读 · 0 评论 -
[NOI1999] 棋盘分割 —— 二维区间DP
题面题目思路用f(x1,y1,x2,y2,k)f(x1,y1,x2,y2,k)f(x1,y1,x2,y2,k)表示将子矩阵(x1,y1)(x2,y2)(x1,y1)(x2,y2)(x1,y1)(x2,y2)切成k部分的所有方案数我们从中取一个min因为我们可以横着切 也可以竖着切;我们先横着切;切一刀以后分成上下两半;我们可以选择其中的一边接着切,另一边可以直接用二维前缀和来计算;竖着切同理;注意这里一个二维坐标代表一个方格,比如下图是8∗88*88∗8的棋盘则有下面的代码原创 2021-10-14 23:25:35 · 170 阅读 · 0 评论 -
加分二叉树——区间DP + 输出方案
题面加分二叉树分析因为要输出先序遍历,因此我们只需要存某个区间的根节点是谁即可;然后按二叉树的先序遍历去dfs即可;又因为要字典序最大,只需要维护最左边且最大的值即可;维护最大值的话,很容易想到枚举断点,将断点看为根节点,去划分左右子树;f[l][r]=f[l][k−1]+f[k+1][r],k为根节点f[l][r] = f[l][k-1]+f[k+1][r],k为根节点f[l][r]=f[l][k−1]+f[k+1][r],k为根节点;Code#include <iostre原创 2021-10-12 18:22:39 · 96 阅读 · 0 评论 -
背包问题几种情况的初始化(至少、至多、恰好、求数量)
简单介绍至多是我们最常见的情况,通常描述为,不超过xxx;而恰好以及至少是比较不常见的;二维情况1、体积至多j,f[i,k]=0,0<=i<=n,0<=k<=mj,f[i,k] = 0,0 <= i <= n, 0 <= k <= mj,f[i,k]=0,0<=i<=n,0<=k<=m(一般只会求价值的最大值)2、体积恰好jjj,当求价值的最小值:f[0][0]=0f[0][0] = 0f[0][0]=0, 其余是INFIN原创 2021-07-24 18:21:18 · 783 阅读 · 0 评论 -
多重背包的几种写法&&完全背包思路
题面思路一、直接枚举二维写法#include <iostream>using namespace std;const int N = 1e2+10;int n,m,f[N][N],v,w,s;int main(){ cin >> n >> m; for(int i=1;i<=n;++i){ cin >> w >> v >> s; for(int j=0;j&l原创 2021-07-21 20:15:27 · 428 阅读 · 1 评论 -
LIS变形——AcWing1014登山
AcWing1014 登山LIS变形题,枚举山顶;如果不预处理的话是需要n3n^3n3的,或者说用nlogn的方法求也要n2lognn^2lognn2logn预处理一下即可注意1->n 的严格降序 不等价于 n->1 的严格升序结果是一样的,但是对于某个点来说,是不同的;即结果相同,过程不同;比如写成这样#include <iostream>using namespace std;const int N = 1010;int a[N];int f[N]原创 2021-05-17 20:08:28 · 106 阅读 · 0 评论 -
方格取数
AcWing 1027. 方格取数分开走(错误)见如下代码#include <iostream>#include <cstring>using namespace std;const int N = 50;int a[N][N];int f[N][N];int pre[N][N];//1表示从上面来 0表示从左边来int main(){ int n; cin >> n; int i,j,c; while(cin&原创 2021-05-15 15:38:16 · 94 阅读 · 0 评论 -
货币系统——完全背包应用
货币系统思路因为题目想让m最小,那首先得符合题目的等价条件;当m=n当m=n当m=n,a[i]=b[i]时a[i]=b[i]时a[i]=b[i]时,必然可以;接着我们想缩小这个m;怎么缩小呢?如果存在某个a[i]a[i]a[i]能被其他的a[i]a[i]a[i]表示,那么这个a[i]a[i]a[i]就是多余的我们对a[i]a[i]a[i]排序,因为大的a[i]a[i]a[i]肯定可以从小的a[i]a[i]a[i]转移过来利用完全背包,f[i][j]表示前i个数,总和为j的时候能否被表示f[原创 2021-07-28 18:26:40 · 86 阅读 · 0 评论 -
最长不下降子序列——LIS变形
题目不断的滑动窗口L,分别求窗口L左边的LIS,以及求右边的LIS;枚举窗口L滑动的过程中,求一个ans,ans是左边的LIS+右边的LIS;使得ans最大即可;右边的LIS我们倒着求,且用负的;这样从左往右看的时候,就知道有多少个数大于等于我们当前的数了;倒着是求LIS方便,否则要变形一下;#include <iostream>#include <cstring>#include <algorithm>#include <string>原创 2021-07-27 17:34:09 · 98 阅读 · 0 评论