【笔记】入门DP
复习一下近期练习的入门 \(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的更多相关文章
- HDU 1231 最大连续子序列 --- 入门DP
HDU 1231 题目大意以及解题思路见: HDU 1003题解,此题和HDU 1003只是记录的信息不同,处理完全相同. /* HDU 1231 最大连续子序列 --- 入门DP */ #inclu ...
- PHP学习笔记 - 入门篇(5)
PHP学习笔记 - 入门篇(5) 语言结构语句 顺序结构 eg: <?php $shoesPrice = 49; //鞋子单价 $shoesNum = 1; //鞋子数量 $shoesMoney ...
- PHP学习笔记 - 入门篇(4)
PHP学习笔记 - 入门篇(4) 什么是运算符 PHP运算符一般分为算术运算符.赋值运算符.比较运算符.三元运算符.逻辑运算符.字符串连接运算符.错误控制运算符. PHP中的算术运算符 算术运算符主要 ...
- PHP学习笔记 - 入门篇(3)
PHP学习笔记 - 入门篇(3) 常量 什么是常量 什么是常量?常量可以理解为值不变的量(如圆周率):或者是常量值被定义后,在脚本的其他任何地方都不可以被改变.PHP中的常量分为自定义常量和系统常量 ...
- PHP学习笔记--入门篇
PHP学习笔记--入门篇 一.Echo语句 1.格式 echo是PHP中的输出语句,可以把字符串输出(字符串用双引号括起来) 如下代码 <?php echo "Hello world! ...
- LESS学习笔记 —— 入门
今天在网上完成了LESS的基础学习,下面是我的学习笔记.总共有三个文件:index.html.main.less.mian.css,其中 mian.css 是 main.less 经过Koala编译之 ...
- HDU 2571 命运 (入门dp)
题目链接 题意:二维矩阵,左上角为起点,右下角为终点,如果当前格子是(x,y),下一步可以是(x+1,y),(x,y+1)或者(x,y*k) ,其中k>1.问最大路径和. 题解:入门dp,注意负 ...
- 【C/C++】日期问题/算法笔记/入门模拟
最近把算法竞赛入门经典的前半部分看完了,开始看算法笔记入门算法. 看了前半部分的例题,很多是算法竞赛入门经典中出现过的,但是感觉这本书写的更适合初学者,而且真的很像考试笔记,通俗易懂. //日期问题 ...
- 【学习笔记】dp入门
知识点 动态规划(简称dp),可以说是各种程序设计中遇到的第一个坎吧,这篇博文是我对dp的一点点理解,希望可以帮助更多人dp入门. 先看看这段话 动态规划(dynamic programming) ...
随机推荐
- 简易的AutoPlayCarousel 轮播控件
原理是使用StackPanel 的margin属性的偏移来实现轮播的效果 废话不多说直接上代码 AutoPlayCarousel核心代码 [ContentProperty(nameof(Childre ...
- MySQL递归查询语法
业务上有一个递归查询数据表进行累加计算的需求,实现方式上有函数.SQL语句等多种方式,最后选择了SQL方式,如下: <select id="selectChildren" p ...
- Android Kotlin Annotation Processer
Annotation Processer 注解处理器(Annotation Processer)是javac内置的注解处理工具,可以在编译时处理注解,让我们自己做相应的处理.比如生成重复度很高的代码, ...
- 第八十八篇:Vue keep-alive的使用 让组件"活下去""
好家伙, 1.关于keep-alive 这是一个用于阻止组件自行销毁的插件 <!-- keep-alive可以把内部组件进行缓存,而不是销毁组件 --> 那么我们什么时候会用到他呢? 举个 ...
- Sqoop 组件安装与配置
下载和解压 Sqoop Sqoop相关发行版本可以通过官网 https://mirror-hk.koddos.net/apache/sqoop/ 来获取 安装 Sqoop组件需要与 Hadoop环境适 ...
- Qt5.14.2使用虚拟键盘
说明 这是关于Qt5(Qt5.1.4.2),QWidget编程使用Qt虚拟键盘(qtvirtualkeyboard) Tag: QT5,Qt,软件盘.虚拟键盘,Widget程序,QML 作者:474 ...
- vscode主题开发
vscode主题开发教程 https://blog.csdn.net/Suwanqing_su/article/details/105945290 个人配置结果 主题代码 到Vscode放插件的目录中 ...
- 助力培养高质量AI人才,璞公英乐学平台在日本深受好评!
璞公英乐学平台(原名"璞睿魔数")自进入日本市场以来,受到日本用户的广泛好评.近日,日本AI门户网站AIsmiley在发刊的杂志<AI人才育成指南>中对璞公英乐学平台做 ...
- Java的线程状态
在我们平时写code的时候,经常会使用到多线程.其中线程所处的状态就是我们需要进程思考的问题. 线程有哪些状态 NEW: 一个线程刚被创建,但是没有被使用就是处于这个状态 RUNNABLE: 一个线程 ...
- ELasticsearch忘记密码后重置超级用户密码
创建一个临时的超级用户TestSuper: [root@cfeea elasticsearch]# ./bin/elasticsearch-users useradd TestSuper -r sup ...