复习一下近期练习的入门 \(DP\) 。巨佬勿喷。\(qwq\)

重新写一遍练手,加深理解。

代码已经处理,虽然很明显,但请勿未理解就贺 \(qwq\)

0X00 P1057 [NOIP2008 普及组] 传球游戏

设 \(f[i][j]\) 表示传球 \(i\) 次后传到第 \(j\) 个人的方案数。

设小蛮为 \(1\) 号,则初始化 \(f[1][n]=1\),\(f[1][2]=1\)。

转移方程为 \(f[i][j]=f[i-1][j-1]+f[i-1][j+1]\) 只需特判 \(1\) 的左边变为 \(n\) ,\(n\) 的右边变为 \(1\) 即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int f[35][35],n,m;
int turn(int x){
return x==n+1?1:(x==0?n:x);
}
int main(){
scanf("%d%d",&n,&m);
f[1][n]=f[1][2]=1;
for(int i=2;i<=m;i++){
for(int j=1;j<=n;j++){
f[i][j]=f[i-1][turn(j-1)]+f[i-1][turn(j+1)];
}
}
printf("%d",f[m][1]);
return 73;
}

0X01 P1060 [NOIP2006 普及组] 开心的金明

emmm。背包板题。纯属练手。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,t,v[35],p[35],f[30005];
int main(){
scanf("%d%d",&t,&n);
for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&p[i]);
for(int i=1;i<=n;i++){
for(int j=t;j>=v[i];j--) f[j]=max(f[j],f[j-v[i]]+p[i]*v[i]);
}
printf("%d",f[t]);
return 76;
}

0X02 P1509 找啊找啊找GF

不得不说题面有点那啥

二维的背包,因为有 \(rmb\) 和 \(rp\) 两个条件要考虑。

同时这题也需要两个 \(DP\) 数组。一个 \(fn[i][j]\) 记录:花费 \(i\) 的 \(rmb\) , \(j\) 的 \(rp\) 可以约的 MM 数量。另一个 \(ft[i][j]\) 记录:花费 \(i\) 的 \(rmb\) , \(j\) 的 \(rp\) 最小的花费时间。

因为数组变量太长了看着不舒服,所以我把转移写成了这样:

int t1=fn[j-rmb[i]][k-rp[i]]+1,t2=ft[j-rmb[i]][k-rp[i]]+tim[i];
if(fn[j][k]<t1){ //如果人数更多就一定更优
fn[j][k]=t1;
ft[j][k]=t2;
}
if(fn[j][k]==t1) ft[j][k]=min(ft[j][k],t2); //人数相等看能不能更新ft

幸好我没有 sqybi 这种痛苦

Code:

#include<bits/stdc++.h>
using namespace std;
int n,rmb[105],rp[105],tim[105],m,r;
int fn[105][105],ft[105][105];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d",&rmb[i],&rp[i],&tim[i]);
scanf("%d%d",&m,&r);
for(int i=1;i<=n;i++){
for(int j=m;j>=rmb[i];j--){
for(int k=r;k>=rp[i];k--){
int t1=fn[j-rmb[i]][k-rp[i]]+1,t2=ft[j-rmb[i]][k-rp[i]]+tim[i];
if(fn[j][k]<t1){
fn[j][k]=t1;
ft[j][k]=t2;
}
if(fn[j][k]==t1) ft[j][k]=min(ft[j][k],t2);
}
}
}
printf("%d",ft[m][r]);
return 111;
}

0X03 P1091 [NOIP2004 提高组] 合唱队形

用 \(DP\) 跑一遍到每个点的最大上升子序列,再跑一遍倒序的最大下降子序列,最后求可以留下来的 \(max\) ,用 \(n\) 减掉即可。

用 \(f[0][i]\) 表示最大上升子序列, \(f[1][i]\) 表示最大下降子序列。因为相等的也会出列,所以注意不加等号。

同时也可以用这个思路过 未加强的导弹拦截

Code:

#include<bits/stdc++.h>
using namespace std;
int n,a[105],f[2][105],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
if(a[i]>a[j]) f[0][i]=max(f[0][i],f[0][j]+1);
}
}
for(int i=n;i>=1;i--){
for(int j=n+1;j>i;j--){
if(a[i]>a[j]) f[1][i]=max(f[1][i],f[1][j]+1);
}
}
for(int i=1;i<=n;i++) ans=max(f[0][i]+f[1][i]-1,ans);
printf("%d",n-ans);
return 118;
}

0X04 P1279 字串距离

用 \(f[i][j]\) 表示 \(A\) 字符串取到前 \(i\) 位, \(B\) 字符串取到前 \(j\) 位时,答案的最小值。

因为求最小值,所以把 \(f\) 初始化为极大值。再把 \(f[0][0]\) 初始化为 \(0\),将所有的 \(f[0][i]\) 与 \(f[i][0]\) 初始化为 \(i \times k\) (\(k\) 为空格与字符匹配代价,也就是初始化全是空格的情况)。

转移时分类讨论:

首先考虑一个字符与一个空格匹配的情况,得出 \(f[i][j]=min(f[i][j],min(f[i][j-1],f[i-1][j])+k)\)。

再考虑两个字符匹配的情况,得出 \(f[i][j]=min(f[i][j],f[i-1][j-1]+abs(a[i]-b[j]))\)。

空格与空格匹配没有意义。

Code:

#include<bits/stdc++.h>
using namespace std;
string x,y;
int a[2005],b[2005],f[2005][2005],n,m,k;
void init(){
for(int i=0;i<n;i++) a[i+1]=x[i];
for(int i=0;i<m;i++) b[i+1]=y[i];
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=max(m,n);i++) f[i][0]=f[0][i]=i*k;
}
int main(){
cin>>x>>y;
n=x.size(),m=y.size();
scanf("%d",&k);
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i][j]=min(f[i][j],min(f[i][j-1],f[i-1][j])+k);
f[i][j]=min(f[i][j],f[i-1][j-1]+abs(a[i]-b[j]));
}
}
printf("%d",f[n][m]);
return 101;
}

注意,双倍经验 ↓ ↓ ↓

0X05 P1140 相似基因

大体思路和上一题相同,只要把匹配的分值改成题目给的表即可。(我才不会告诉你我第一次抄错了)

还要注意一下初始化和转移的小改动。

Code:

#include<bits/stdc++.h>
using namespace std;
int a[105],b[105],f[105][105],n,m;
string x,y;
int t[5][5]{
{5,-1,-2,-1,-3},
{-1,5,-3,-2,-4},
{-2,-3,5,-2,-2},
{-1,-2,-2,5,-1},
{-3,-4,-2,-1,0}
};
void turn(int n,int a[],string s){
for(int i=0;i<n;i++){
if(s[i]=='A') a[i+1]=0;
if(s[i]=='C') a[i+1]=1;
if(s[i]=='G') a[i+1]=2;
if(s[i]=='T') a[i+1]=3;
}
}
void init(){
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++) f[i][0]=f[i-1][0]+t[a[i]][4];
for(int i=1;i<=m;i++) f[0][i]=f[0][i-1]+t[4][b[i]];
}
int main(){
scanf("%d",&n);cin>>x;
scanf("%d",&m);cin>>y;
turn(n,a,x);turn(m,b,y);
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i][j]=max(f[i-1][j]+t[a[i]][4],f[i][j-1]+t[4][b[j]]);
f[i][j]=max(f[i][j],f[i-1][j-1]+t[a[i]][b[j]]);
}
}
printf("%d",f[n][m]);
return 67;
}

0X06 P1006 [NOIP2008 提高组] 传纸条

可以看成是走两条不相交的,从 \((1,1)\) 到 \((n,m)\) 的路径的最大值。

设 \(f[i][j][x][y]\) 表示第一条走到 \((i,j)\),第二条走到 \((x,y)\) 时的最大值。

转移方程:

\(f[i][j][x][y]=\)

$max {f[i-1][j][x-1][y],f[i][j-1][x][y-1],

f[i][j-1][x-1][y],f[i-1][j][x][y-1] } $

\(+a[i][j]+a[x][y]\)

注意,因为两条路不能走到同一个点,所以当 \(i=x\) 并且 \(j=y\) 时,\(f[i][j][x][y]\) 要减掉 \(a[i][j]\)。

Code:

#include<bits/stdc++.h>
using namespace std;
int f[55][55][55][55],a[55][55],n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int x=1;x<=n;x++){
for(int y=1;y<=m;y++){
int t=max(max(f[i-1][j][x-1][y],f[i][j-1][x][y-1]),
max(f[i][j-1][x-1][y],f[i-1][j][x][y-1]));
f[i][j][x][y]=t+a[i][j]+a[x][y];
if(i==x&&j==y) f[i][j][x][y]-=a[i][j];
}
}
}
}
printf("%d",f[n][m][n][m]);
return 89;
}

注意,双倍经验 ↓ ↓ ↓

0X07 P1004 [NOIP2000 提高组] 方格取数

前一题题意化简版,范围也更小。改一下读入和输出即可。

#include<bits/stdc++.h>
using namespace std;
int f[15][15][15][15],a[15][15],n;
int u,v,c;
int main(){
scanf("%d",&n);
for( ; ; ){
scanf("%d%d%d",&u,&v,&c);
if(u==0&&v==0&&c==0) break;
a[u][v]=c;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int x=1;x<=n;x++){
for(int y=1;y<=n;y++){
int t=max(max(f[i-1][j][x-1][y],f[i][j-1][x][y-1]),
max(f[i][j-1][x-1][y],f[i-1][j][x][y-1]));
f[i][j][x][y]=t+a[i][j]+a[x][y];
if(i==x&&j==y) f[i][j][x][y]-=a[i][j];
}
}
}
}
printf("%d",f[n][n][n][n]);
return 89;
}

0X08 P1435 [IOI2000]回文字串/[蓝桥杯2016省]密码脱落

设 \(f[i][j]\) 表示 将从 \(i\) 到 \(j\) 的字串变为回文最少要插入的字符数。

此时我们只需要枚举区间长度 \(k\) 和 左端点 \(i\),可以计算出右端点 \(j=i+k\)(直接枚举 \(i\) 和 \(j\) 是错误的)。

由此可以得出对于区间 \((i,j)\) 的转移方程:

若 \(s[i]=s[j]\):\(f[i][j]=f[i+1][j-1]\) (不用改)

若 \(s[i] \ne s[j]\) :\(f[i][j]=min(f[i+1][j],f[i][j-1])+1\) (取较小值加一次)

Code:

#include<bits/stdc++.h>
using namespace std;
int f[1005][1005],n;
string s;
int main(){
cin>>s;
n=s.size();
for(int i=n;i>=1;i--) s[i]=s[i-1];
for(int k=1;k<n;k++){
for(int i=1;i<=n-k;i++){
int j=i+k;
if(s[i]==s[j]) f[i][j]=f[i+1][j-1];
else f[i][j]=min(f[i+1][j],f[i][j-1])+1;
}
}
printf("%d",f[1][n]);
return 33;
}

【笔记】入门DP的更多相关文章

  1. HDU 1231 最大连续子序列 --- 入门DP

    HDU 1231 题目大意以及解题思路见: HDU 1003题解,此题和HDU 1003只是记录的信息不同,处理完全相同. /* HDU 1231 最大连续子序列 --- 入门DP */ #inclu ...

  2. PHP学习笔记 - 入门篇(5)

    PHP学习笔记 - 入门篇(5) 语言结构语句 顺序结构 eg: <?php $shoesPrice = 49; //鞋子单价 $shoesNum = 1; //鞋子数量 $shoesMoney ...

  3. PHP学习笔记 - 入门篇(4)

    PHP学习笔记 - 入门篇(4) 什么是运算符 PHP运算符一般分为算术运算符.赋值运算符.比较运算符.三元运算符.逻辑运算符.字符串连接运算符.错误控制运算符. PHP中的算术运算符 算术运算符主要 ...

  4. PHP学习笔记 - 入门篇(3)

    PHP学习笔记 - 入门篇(3) 常量 什么是常量 什么是常量?常量可以理解为值不变的量(如圆周率):或者是常量值被定义后,在脚本的其他任何地方都不可以被改变.PHP中的常量分为自定义常量和系统常量 ...

  5. PHP学习笔记--入门篇

    PHP学习笔记--入门篇 一.Echo语句 1.格式 echo是PHP中的输出语句,可以把字符串输出(字符串用双引号括起来) 如下代码 <?php echo "Hello world! ...

  6. LESS学习笔记 —— 入门

    今天在网上完成了LESS的基础学习,下面是我的学习笔记.总共有三个文件:index.html.main.less.mian.css,其中 mian.css 是 main.less 经过Koala编译之 ...

  7. HDU 2571 命运 (入门dp)

    题目链接 题意:二维矩阵,左上角为起点,右下角为终点,如果当前格子是(x,y),下一步可以是(x+1,y),(x,y+1)或者(x,y*k) ,其中k>1.问最大路径和. 题解:入门dp,注意负 ...

  8. 【C/C++】日期问题/算法笔记/入门模拟

    最近把算法竞赛入门经典的前半部分看完了,开始看算法笔记入门算法. 看了前半部分的例题,很多是算法竞赛入门经典中出现过的,但是感觉这本书写的更适合初学者,而且真的很像考试笔记,通俗易懂. //日期问题 ...

  9. 【学习笔记】dp入门

    知识点 动态规划(简称dp),可以说是各种程序设计中遇到的第一个坎吧,这篇博文是我对dp的一点点理解,希望可以帮助更多人dp入门.   先看看这段话 动态规划(dynamic programming) ...

随机推荐

  1. openstack中Glance组件简解

    一.Glance组件介绍 1.概念 Glance是OpenStack镜像服务,用来注册.登陆和检索虚拟机镜像.Glance服务提供了一个REST API,使你能够查询虚拟机镜像元数据和检索的实际镜像. ...

  2. KingbaseES V8R6 维护管理案例之---Kstudio在CentOS 7启动故障

    ​ 案例说明: 在CentOS 7上安装KingbaseES V8R6C006数据库后,启动Kstudio图形界面启动失败,gtk动态库加载失败,安装gtk相关动态库后,问题解决. 适用版本: Kin ...

  3. 本地 maven + scala 跑spark wordcount

    pom.xml 点击查看代码 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http: ...

  4. Mysql_索引总结笔记

    Mysql 索引总结 1. 聚簇索引 InnoDB 引擎使用的就是聚簇索引,就是主键的索引,是一种数据的存储方式.所有的数据都是存储在索引的叶子结点上(与MySAM 引擎不同,MySAM是传统方式), ...

  5. 在 Kubernetes 中部署 Redis 集群

    在 Kubernetes 中部署 Redis 集群 在Kubernetes中部署Redis集群面临挑战,因为每个 Redis 实例都依赖于一个配置文件,该文件可以跟踪其他集群实例及其角色.为此,我们需 ...

  6. 【Elasticsearch】ES选主流程分析

    Raft协议 Raft是分布式系统中的一种共识算法,用于在集群中选举Leader管理集群.Raft协议中有以下角色: Leader(领导者):集群中的领导者,负责管理集群. Candidate(候选者 ...

  7. k8s中计算资源策略 Limit Range

    文章转载自:https://www.kuboard.cn/learning/k8s-advanced/policy/lr.html 默认情况下,容器在 Kubernetes 集群上运行时,不受 计算资 ...

  8. Elastic:Sense chrome - 在 chrome 浏览器中操作 Elasticsearch 的插件

    文章转载自:https://elasticstack.blog.csdn.net/article/details/114533904 Sense 是我们现在所说的 Kibana 控制台 UI 的第一个 ...

  9. gitlab cicd流水线语法

    流水线语法有哪些? 流水线参数列表 Keyword Description script 运行的Shell命令或脚本. image 使用docker映像. services 使用docker服务映像. ...

  10. 工作7年收集到的git命令

    概念 git 中的术语解释: 仓库也叫版本库(repository) stage:暂存区,add 后会存到暂存区,commit 后提交到版本库 git 安装 linux 下安装 git 第一种方法:y ...