DP刷题记录

目录

  • dp刷题记录

    • codeforces 706C
    • codeforces 940E
    • BZOJ3997
    • POJ2279
    • GYM102082B
    • GYM102082D
    • codeforces132C
    • L3-020 至多删三个字符
    • 牛客 553C Chino with Queue
    • POJ3260 The Fewest Coins
    • Codeforces 372C

dp刷题记录

codeforces 706C

题意:给出n个字符串,可以对每个字符串进行翻转操作,
每个操作对应一个消耗c[i],问经过操作后是否能满足字符串str[i]>=str[[i-1],能输出最小花费,不能输出-1

dp [i] [0]表示前i个字符串已经排好,最后一个字符串没翻转,dp[i] [1]表示前i个字符串已经排好,最后一个字符串翻转

边界:
\[
dp[1][0]=0;
dp[1][1]=c[1];
\]
方程:
\[
方程= \begin{cases}
dp[i][0]=min(dp[i][0],dp[i-1][0]),\quad str[i] >= str[i - 1]\dp[i][0]=min(dp[i][0],dp[i-1][1]),\quad str[i] >= restr[i-1]\dp[i][1]=min(dp[i][1],dp[i-1][0]+c[i]),\quad restr[i]>=str[i-1]\dp[i][1]=min(dp[i][1],dp[i-1][1]+c[i]),\quad restr[i]>=restr[i-1]\\end{cases} \\]
结果:
\[
min(dp[n][0], dp[n][1])
\]

codeforces 940E

题意:给你一个长度为n的序列和一个数c,你可以将序列划分成很多段,若其子序列的长度为k,那么就删除其中[k/c](向下取整)小的值,问你这些子序列最小的和是多少

dp[i]表示前i个数可以删掉的最大和
\[
dp[i]=max(dp[i-1],dp[i-c]+min(a[k]))\quad i-c<k<=i
\]
单调队列优化,将O(n^2)优化为O(nlogn)

其中单调队列处理的是前c个数字中的最小的数

目标:
\[
sum-dp[n]
\]

BZOJ3997

题意:给我一个网格图,每次只能往下走或者往右走,经过每个格子是只能拿走一块财宝,问走多少次可以将财宝减完

Dilworth定理:DAG的最小链覆盖=最大点独立集
最小链覆盖指选出最少的链(可以重复)使得每个点都在至少一条链中
最大点独立集指最大的集合使集合中任意两点不可达

需要补充图论知识

将每一个财宝抽象成一个点,这就变成了一个有向无环图的最小路径覆盖

对于这类问题,我们知道最长反链=最小路径覆盖,所以求出这张图的最长反链就可以了。

对于一个点,它的左上角是可以到达的,但是右上角不能,所以用f[i][j] 表示以i,j为左下角的矩形的最长反链,则
\[
f[i][j]=max(f[i][j+1],f[i-1][j],f[i-1][j+1]+a[i][j]).
\]

POJ2279

题意:有n个学生合影,站成左端对齐的k排,每排有ai个人,第一排站最后面,第k排站最前面,学生的身高互不相同,把他们的身高依次标记为1到n,要求合影时每一排从左到右身高递减、每一列从后到前身高也递减,问一共有多少种合影方案

杨氏矩阵又叫杨氏图表,它是这样一个矩阵,满足条件:

(1)如果格子(i,j)没有元素,则它右边和上边的相邻格子也一定没有元素。

(2)如果格子(i,j)有元素a[i][j],则它右边和上边的相邻格子要么没有元素,要么有元素且比a[i][j]大。

1 ~ n所组成杨氏矩阵的个数可以通过下面的递推式得到

$$
F(1)=1,F(2)=2\
F(n)=F(n-1)+(n-2)*F(n-2),(n>2)\
对于给定形状,不同的杨氏矩阵的个数为:n!除以每个格子的钩子长度加1的积。
\其中钩子长度定义为该格子

右边的格子数和它上边的格子数之和。\

钩子公式:res=n! / (hock[1]hock[2].....hock[n]);
$$

\[
dp[i][j][k][p][q]表示各排从左端起分别站了i,j,k,p,q个人时合影的方案数量\边界:dp[0][0][0][0][0]=1\转移:
方程=\begin{cases}
dp[i + 1][j][k][p][q] += dp[i][j][k][p][q];\quad i + 1 <= a[1]\\\ dp[i][j + 1][k][p][q] += dp[i][j][k][p][q];\quad j + 1 <= a[2] && j < i\\\ dp[i][j][k + 1][p][q] += dp[i][j][k][p][q];\quad k + 1 <= a[3] && k < j && k < i\\\ dp[i][j][k][p + 1][q] += dp[i][j][k][p][q];\quad p + 1 <= a[4] && p < i && p < j && p < k\\\ dp[i][j][k][p][q + 1] += dp[i][j][k][p][q];\quad q + 1 <= a[5] && q < i && q < j && q < k && q < p\\\ \end{cases}
\]

GYM102082B

题意:求n个数中最长的等差数列长度
\[
dp[i][j]表示以a[j]和a[i]为结尾的等差序列的最长长度为多少\\]
因为我们肯定要保存一个公差作为状态量,但是直接枚举0-1e9又不现实。所以我们巧妙的设计出了这个状态,使得我们的公差就被a[i]-a[j]表示了。
因此我们的转移就应该是在三个数中转移。
即a[i],a[j],a[k],i<j<k,但是直接枚举i,j,k也是不现实的
根据等差数列的性质,若a[i],a[j],a[k]构成等差序列,那么a[i]+a[k]=2*a[j];
\[
dp[k][j]=dp[j][i]+1,\quad dp[j][i]!=0\dp[k][j]=3,\quad dp[j][i]==0\\
\]
每次转移更新答案即可

我们枚举j和k,那么i可以用双指针给处理出来从而让复杂度将为n^2的级别

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5005;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn];
int a[maxn];
int main() {
 int n;
 scanf("%d", &n);
 for(int i = 1; i <= n; i++) {
     scanf("%d", &a[i]);
 }
 sort(a + 1, a + n + 1);
 int ans = 2;
 for(int i = 1; i < n; i++) {
     int j = i - 1;
     for(int k = i + 1; k <= n && j; k++) {
         while(j && a[j] + a[k] > a[i] + a[i]) {
             j--;
         }
         if(j && a[j] + a[k] == a[i] + a[i]) {
             if(dp[i][j] == 0) {
                 dp[k][i] = 3;
             } else {
                 dp[k][i] = dp[i][j] + 1;
             }
             ans = max(dp[k][i], ans);
         }
     }
 }
 cout << ans << endl;
}

GYM102082D

SCNS(最短非公共子序列模板题)

最短非公共子序列:给你两个序列,求出不属于这两个序列的最短的子序列

解法:求出每个序列的每个位置后的第一个0,1的位置
\[
dp[i][j][0/1]表示,在第一个序列的x位置和在第二个序列的y位置后,填上0/1是否满足非公共子序列
\]
待补!!!!

codeforces132C

给一个序列,有TF,F代表机器人向前走一步,
T代表机器人转身,再给你一个N问改变N个字母后机器人最多可以走多远,
每个字母可以改变多次
\[
dp[i][j][k][2]表示前i个字母,改变了j次,当前停留在k位置,方向为0/1时的状态是否存在\边界:dp[0][0][len][1]=1;\\方程=\begin{cases}
dp[i][j][k][!d]=1;\quad s[i]=='T'\ dp[i][j][k+1][d]=1;\quad s[i]=='F',d==1\ dp[i][j][k-1][d]=1;\quad s[i]=='F',d==0\ dp[i][j+1][k][!d]=1;\quad j<n,s[i]='F'\ dp[i][j+1][k+1][d]=1;\quad j<n,s[i]='T',d==1\ dp[i][j+1][k-1][d]=1;\quad j<n,s[i]='T',d==0\\end{cases}
\]

\[
dp[i][j][k][2]表示前i个字母,改变了j次,当前停留在k位置,方向为0/1时的状态是否存在\边界:dp[0][0][len][1]=1;\\方程=\begin{cases}
dp[i][j][k][!d]=1;\quad s[i]=='T'\ dp[i][j][k+1][d]=1;\quad s[i]=='F',d==1\ dp[i][j][k-1][d]=1;\quad s[i]=='F',d==0\ dp[i][j+1][k][!d]=1;\quad j<n,s[i]='F'\ dp[i][j+1][k+1][d]=1;\quad j<n,s[i]='T',d==1\ dp[i][j+1][k-1][d]=1;\quad j<n,s[i]='T',d==0\\end{cases}
\]

L3-020 至多删三个字符

题意:给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串
\[
状态设计:dp_{i,j}表示到第i个字符删除了j个字符后得到的字符串种类\因为可能存在重复情况,去重的状态转移\在这个状态之前,如果有重复字符被删除了的话,根据容斥关系,我们应该把重复加的情况给删除,\\从当前位置往前扫一遍即可解决\边界:dp_{0.0}=1\dp_{i,j}=\begin{cases}
dp_{i,j+1}=dp_{i-1,j};\quad j+1<=3\ dp_{i,j}=dp_{i,j}+dp_{i-1,j} \quad\\end{cases}
\]
目的:
\[
ans=\sum_{i=1}^{3}dp_{n,i}
\]

牛客 553C Chino with Queue

题意: 让j排在i之前,i会获得一个舒适度W_i_j,求排队可以得到的最大的舒适度

题解:n为18,非常明显的状压

我们先预处理出每一个状态中1的个数,将相同1的个数的状态放在一个vector中,然后按顺序遍历状态数组vector,这样就可以按0~n的顺序转移
\[
状态:dp_{sta,j}表示状态为sta时以第j个人为当前队尾时状态z的最大舒适度\目的:max_{1<=i<=n}(dp[1<<n][i])\转移:\max(dp[sta + (1 << k)][k], dp[sta][j] + mp[k][j]);((sta >> j)\&1==1),(sta>>k)\&1==0
\]

POJ3260 The Fewest Coins

题意:你有n种钞票,面值为c[i],数量为v[i],便利店老板有无数张面值为c[i]的钞票,问你买一个价值为T的物品,最少需要经手多少张钞票,老板找零的钞票数也算经手的钞票数

题解:因为我的钞票是有限的,所以将自己看作一个多重背包,老板的钞票是无限的,所以将老板的钞票看做一个完全背包
\[
定义状态dp[i]最少花费多少张钞票可以买价值为i的物品\边界:dp1[0]=dp2[0]=0;\目的:ans=min(dp1[i]+dp2[i-v])\quad i>=v;\转移\dp1为完全背包的转移,表示自己凑成面值为i的物品需要的最少钞票张数\dp2为多重背包转移,表示老板凑成面值为i的物品需要的最少的钞票张数\那么买面值为T的物品所需要的最少的钞票数就是min(dp1[T],min_{T\sim maxn}(dp2_{i-V}+dp1_i))
\]

Codeforces 372C

题意:在长度为n的街道上,将有m个烟花燃放,每个烟花有a,b,t三种属性,表示在t时间,在地点a燃放烟花,一个人可以获得b_i-|a_i-x|的愉悦值,单位时间内,一个人最多移动的距离为d,在起始时,他可以在任意点,问这个人最多可以获得的愉悦值是多少

题解:
\[
按照t从小到大排序\状态:dp_{i,j}为看了前i个烟花时走到位置j时可以得到的最大的愉悦值\转移:dp_{i,j}=max(dp_{i-1,k}+b_i-abs(a_i-x_i))\由于空间开不下,并且我们的转移一直只由上一个状态转移过来\我们可以使用滚动数组将第一维滚掉\新状态:dp_{0|1,j}=max(dp_{1|0,k}+b_i-abs(a_i-x_i))\由于每次转移与之前保存的max状态有关,我们可以用单调队列优化掉哪些重复的转移\\]

原文地址:https://www.cnblogs.com/buerdepepeqi/p/10841105.html

时间: 2024-10-15 03:35:19

DP刷题记录的相关文章

BZOJ 刷题记录 PART 4

[BZOJ1143]CTSC的题目...先用floyed传递闭包,然后直接上匈牙利算法. [BZOJ1452]从未写过的二维树状数组.好像很简单.. struct two_bit { int f[305][305]; inline void add(int x,int z,int A) { for (;x<=n;x+=L(x)) for (int y=z;y<=m;y+=L(y)) f[x][y]+=A; } inline int ask(int x,int z) { int ans=0; f

openjudge dp水题记录

当发现自己竟然不会打dp的时候内心是崩溃的,然后按照一年前的刷题记录刷openjudge,然后发现自己准确率比一年前(刚学信竞两个月时)的准确率低得多,已经没救. 列一下最近打的几道sb题 2985:数字组合 描述有n个正整数,找出其中和为t(t也是正整数)的可能的组合方式.如: n=5,5个数分别为1,2,3,4,5,t=5: 那么可能的组合有5=1+4和5=2+3和5=5三种组合方式.输入输入的第一行是两个正整数n和t,用空格隔开,其中1<=n<=20,表示正整数的个数,t为要求的和(1&

[2015.6.28] OI刷题记录

FZSZOJ刷题记录: 1051 砝码称重: DP 多重背包 1058 liqeuer: 序列DP 1061 挖地雷:DP,注意需要倒过来做DP,同时记录路径. 1059 Number:DP 1054 数塔问题:同数字三角形,普通DP 1390 等式问题:爆搜,枚举每个+号或-号的位置 1006 中位数:维护大根堆+小根堆,每次插入调整 1005 Cube Stacking:并查集维护当前在第几个和当前集合的高度,并查集变种. 1073 DNA分子的最佳比对:序列DP 1110 奖学金:傻逼题,

BZOJ 刷题记录 PART 5

拖了好久才写的. [BZOJ2821]接触分块大法.这道题略有点新颖.首先我们先分块,然后统计每块中每个数出现的个数. 下面是联立各个方块,预处理出第I个方块到第J个方块出现正偶数次数的个数. for (i=1;i<=s;i++) { for (j=i;j<=s;j++) { sum[i][j]=sum[i][j-1]; for (k=a[j].l;k<=a[j].r;k++) { temp[data[k]]++; if (!(temp[data[k]]&1)) sum[i][j

leetcode刷题记录(2)

301. Remove Invalid Parentheses Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note: The input string may contain letters other than the parentheses ( and ). Examples: "()())()&q

BZOJ 刷题记录 PART 2

[前言]最近感觉状态不错.做题几乎不看题解了.(一群大牛(FZ&WCY)在旁边喷:你刷水题有意思!)但是至少这也是一种进步吧.特别是权限题中有很多思维题. [BZOJ1055]就是一个简单的区间DP.重要代码: for (l=2;l<=L;l++) for (i=1;i<=L-l+1;i++) { j=i+l-1; for (k=0;k<4;k++) for (cut=i;cut<j;cut++) for (p=0;p<4;p++) if (f[i][cut][p])

BZOJ 刷题记录 PART 3

[前言]还是强调要少看题解. [BZOJ1090]简单的区间DP.值得注意的是:在压缩的时候,如果是10个A压缩,那么化成(10)A后有5个字符而不是4个!(我在这里被坑了好长时间!)以下是核心代码: for (len=2;len<=L;len++) for (i=1;i<=L-len+1;i++) { j=i+len-1; for (k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]); for (l=1;l<=len/2;l++

暑期刷题记录

已经决定不玩空间了,在这里开一贴,用来记录暑假期间刷过的每一题. 时间从7.29号开始计算. 1. HDU 4883 TIANKENG’s restaurant    ( 贪心 ) 这个是bestcoder #2 的第一题,,居然想半天没有做出来,简直是太弱了,居然又在分情况讨论 题目大意:TIANKENG的饭店有客人陆续到达,如果再一批客人没有走的情况下,新来的客人就需要另外的座位,问最少需要多少座位. 题解: 贪心算法,首先对所有时间进行排序(时间相同以人数为第二关键字), 然后如果是到达,

Codeforces 刷题记录

Codeforces 每日刷题记录 打'+'是一些有启发意义的题目,部分附上一句话题解,每日更新3题,大部分题目较水. 1.+CF1073E:状压,数位dp,官方题解std骚操作 2.CF1072A 3.CF1072B 4.CF1072C 5.CF1068C:读题恶心 6.CF1073D:猜复杂度,模拟 7.CF1088A 8.CF1088B 9.CF1088C:构造思想 10.CF1066A 11.CF1066B 12.CF1066C 13.+CF1088E:推结论,tree dp,贪心 14