【笔记】入门DP(Ⅱ)
0X00 P1433 吃奶酪
状压 \(DP\),把经过的点压缩成01串。若第 \(i\) 位为 \(0\) 表示未到达,为 \(1\) 则表示已到达。
用 \(f[i][j]\) 表示以 \(i\) 为起点,经过 \(j\) 所含 \(1\) 位置的所有点的最小距离。
先预处理出点两两之间的距离,记为 \(dis[i][j]\),初始化 \(f\) 数组为极大值(\(memset(f,127,sizeof(f))\) 可以为浮点数赋极大值)。将所有的 \(f[i][1<<(i-1)]\) 赋为 \(dis[0][i]\),也就是原点到它的距离。
转移方程:\(f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+dis[i][j])\)
注意,有前提条件,就是 \(k\&(1<<(i-1)) = 0\),\(k\&(1<<(j-1)) = 0\),意为当前的01串 \(k\) 包含了 \(i\) 点和 \(j\) 点。同时要 \(i \ne j\),这个很好理解。
Code:
#include<bits/stdc++.h>
using namespace std;
double x[20],y[20],dis[20][20],f[20][50005],ans=1e9;
int n;
double calc(int i,int j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void pre(){
for(int i=0;i<n;i++){
for(int j=i+1;j<=n;j++) dis[i][j]=dis[j][i]=calc(i,j);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
memset(f,127,sizeof(f));
pre();
for(int i=1;i<=n;i++) f[i][1<<(i-1)]=dis[0][i];
for(int k=1;k<(1<<n);k++){
for(int i=1;i<=n;i++){
if((k&(1<<(i-1)))==0) continue;
for(int j=1;j<=n;j++){
if(i==j) continue;
if((k&(1<<(j-1)))==0) continue;
f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+dis[i][j]);
}
}
}
for(int i=1;i<=n;i++) ans=min(ans,f[i][(1<<n)-1]);
printf("%.2lf",ans);
return 73;
}
0X01 P1775 石子合并(弱化版)
记 \(f[i][j]\) 表示 \(i\) 到 \(j\) 一段区间合并的最小花费。
可以考虑将一个区间 \(i\) ~ \(j\) 从任意位置 \(k\) 分成两段,再合并的最小值。
可以写出转移方程:
\(f[i][j]=\min \{\) \(f[i][k]+f[k+1][j]\) \(\}\) \(+\sum_{x=i}^{j} a[x]\) \((i\le k\le j)\)
其中 \(\sum_{x=i}^{j} a[x]\) 可以用前缀和求出。
因为求最小值,所以初始化 \(f\) 为极大值,同时将 \(f[i][i]\) 赋为 \(0\)。
Code:
#include<bits/stdc++.h>
using namespace std;
int n,f[1005][1005],a[1005],sum[1005];
int main(){
scanf("%d",&n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i][i]=0;
sum[i]=sum[i-1]+a[i];
}
for(int len=2;len<=n;len++){
for(int i=1;i<=n-len+1;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
}
}
}
printf("%d",f[1][n]);
return 76;
}
0X02 P1799 数列
记 \(f[i][j]\) 表示前 \(i\) 个数,删掉 \(j\) 个时匹配的最大个数。
首先考虑直接删掉这个数, \(f[i][j]=f[i-1][j-1]\),相当于不变。注意:当 \(j>0\) 时才考虑这一种情况。
其次,考虑不删这个数。当 \(a[i]=i-j\) 是,这个数刚好是匹配的。所以 \(f[i][j]=max(f[i][j],f[i-1][j]+(a[i]==i-j))\)
最后答案是所有 \(f[i][j]\) 中的最大值。
Code:
#include<bits/stdc++.h>
using namespace std;
int n,a[1005],f[1005][1005],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(j>0) f[i][j]=f[i-1][j-1];
f[i][j]=max(f[i][j],f[i-1][j]+(a[i]==i-j));
ans=max(ans,f[i][j]);
}
}
printf("%d",ans);
return 111;
}
0X03 P1107 [BJWC2008]雷涛的小猫
记 \(f[i][j]\) 表示小猫在第 \(i\) 层(设高度为 \(h\) 为第 \(1\) 层,高度为 \(1\) 为第 \(h\) 层),第 \(j\) 棵树上时,最多可以吃到的柿子数量。
\(a[i][j]\) 表示第 \(i\) 棵树第 \(j\) 层有几个柿子,所以读入时这样记(为配合 \(f\),有点别扭):
for(int j=1;j<=x;j++) scanf("%d",&y),a[i][h-y+1]++;
两种转移:
- 在同一颗树上下降 \(1\) 单位高度:\(f[i][j]=f[i-1][j]+a[j][i]\)
- 从不同的树下来,下降 \(Delta\) 单位高度:\(f[i][j]=max(f[i][j],f[k][i-de]+a[j][i])\)。有前提条件:\(i>delta\)。
但这样时间复杂度 \(O(n^3)\),显然 \(TLE\)。考虑优化,\(f[k][i-de]\) 这一维可以用 \(mx[i-de]\) 代替,其中 \(mx[i-de]\) 表示 \(\max \{\) \(f[k][i-de]\) \(\}\),是可以边算边更新的。
Code:
#include<bits/stdc++.h>
using namespace std;
int n,h,de,x,y,f[2005][2005],mx[2005];
int a[2005][2005],ans;
int main(){
scanf("%d%d%d",&n,&h,&de);
for(int i=1;i<=n;i++){
scanf("%d",&x);
for(int j=1;j<=x;j++) scanf("%d",&y),a[i][h-y+1]++;
}
for(int i=1;i<=h;i++){
for(int j=1;j<=n;j++){
f[i][j]=f[i-1][j]+a[j][i];
if(i>de) f[i][j]=max(f[i][j],mx[i-de]+a[j][i]);
mx[i]=max(mx[i],f[i][j]);
}
}
for(int i=1;i<=n;i++) ans=max(ans,f[h][i]);
printf("%d",ans);
return 118;
}
0X04 P1115 最大子段和
\(f[i]\) 表示到 \(i\) 为止最大子段和。
转移:\(f[i]=max(f[i-1],0)+a[i]\)
答案为 \(\max \{\) \(f[i]\) \(\}\)
Code:
#include<bits/stdc++.h>
using namespace std;
int n,a[200005],f[200005],ans=-1e9;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
f[1]=a[1];
for(int i=2;i<=n;i++) f[i]=max(0,f[i-1])+a[i],ans=max(ans,f[i]);
printf("%d",ans);
return 101;
}
0X05 环状最大子段和(暂无例题)
分两种情况讨论。
- 子段的头尾在 \(1\) ~ \(n\) 范围内:按上面做。
- 子段横跨了头尾,最大和即为数列总和减去头尾在 \(1\) ~ \(n\) 范围内的最小子段和,求法相似。
记 \(f1\) 为头尾在 \(1\) ~ \(n\) 范围内最大子段和:\(f1[i]=max(f1[i-1]+a[i],a[i])\)
记 \(f2\) 为头尾在 \(1\) ~ \(n\) 范围内最小子段和:\(f2[i]=min(f2[i-1]+a[i],a[i])\)
初始化 \(f1[1]=f2[1]=a[1]\)
Code:
#include<bits/stdc++.h>
using namespace std;
int f1[200005],f2[200005];
int n,a[200005],sum,mx,mn;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
mx=mn=f1[1]=f2[1]=a[1];
for(int i=2;i<=n;i++) f1[i]=max(f1[i-1]+a[i],a[i]),mx=max(mx,f1[i]);
for(int i=2;i<=n;i++) f2[i]=min(f2[i-1]+a[i],a[i]),mn=min(mn,f2[i]);
printf("%d",max(mx,sum-mn));
return 67;
}
0X06 P2642 双子序列最大和
对于每个 \(k(2<k<n)\),我们让它作隔点,计算出其左边的最大子段和与右边最大子段和,相加,最后取 \(\max\) 即可。
每个点左侧的最大子段和记 \(f1[i]\),右侧的最大子段和记 \(f2[i]\),都可以 \(O(n)\) 预处理。
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f1[1000005],f2[1000005];
ll n,a[1000005],ans=-1e18;
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
f1[1]=a[1],f2[n]=a[n];
for(int i=2;i<=n;i++) f1[i]=max(f1[i-1]+a[i],a[i]);
for(int i=2;i<=n;i++) f1[i]=max(f1[i],f1[i-1]);
for(int i=n-1;i>=1;i--) f2[i]=max(f2[i+1]+a[i],a[i]);
for(int i=n-1;i>=1;i--) f2[i]=max(f2[i],f2[i+1]);
for(int i=2;i<n;i++) ans=max(ans,f1[i-1]+f2[i+1]);
printf("%lld",ans);
return 89;
}
0X07 P1121 环状最大两段子段和
环状最大子段和 与 双子序列最大和 的结合体。
需要注意的是,这题中两个子段可以去相邻的,所以更麻烦一些。
- 首先,两子段的头尾在 \(1\) ~ \(n\) 范围内,可以按原来做,只要最后求答案改成可以相邻即可:
for(int i=1;i<n;i++) mx=max(mx,f1[i]+f2[i+1]);
- 其次,两子段其中一段横跨了头尾,这时不用考虑相邻,因为相邻就会变成第一种情况。只要分类讨论,算一遍原数组和原数组最小子段和,整体后移一位的数组的最小子段和,取 \(\min\) 与 \(sum\) 作差即可。
Code:
#include<bits/stdc++.h>
using namespace std;
int f1[200005],f2[200005],f3[200005],f4[200005];
int n,a[200005],sum,mx=-2e9,mn;
int mmn(int k,int n){
int tmp=2e9;
f3[1]=a[1+k],f4[n]=a[n+k];
for(int i=2;i<=n;i++) f3[i]=min(f3[i-1]+a[i+k],min(a[i+k],0));
for(int i=2;i<=n;i++) f3[i]=min(f3[i-1],f3[i]);
for(int i=n-1;i>0;i--) f4[i]=min(f4[i+1]+a[i],min(a[i],0));
for(int i=n-1;i>0;i--) f4[i]=min(f4[i+1],f4[i]);
for(int i=2;i<n;i++) tmp=min(tmp,f3[i-1]+f4[i+1]);
return tmp;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
f1[1]=a[1],f2[n]=a[n];
for(int i=2;i<=n;i++) f1[i]=max(f1[i-1]+a[i],a[i]);
for(int i=2;i<=n;i++) f1[i]=max(f1[i],f1[i-1]);
for(int i=n-1;i>=1;i--) f2[i]=max(f2[i+1]+a[i],a[i]);
for(int i=n-1;i>=1;i--) f2[i]=max(f2[i],f2[i+1]);
for(int i=1;i<n;i++) mx=max(mx,f1[i]+f2[i+1]);
mn=min(mmn(0,n-1),mmn(1,n-1));
printf("%d",max(mx,sum-mn));
return 89;
}
【笔记】入门DP(Ⅱ)的更多相关文章
- 【笔记】入门DP
复习一下近期练习的入门 \(DP\) .巨佬勿喷.\(qwq\) 重新写一遍练手,加深理解. 代码已经处理,虽然很明显,但请勿未理解就贺 \(qwq\) 0X00 P1057 [NOIP2008 普及 ...
- 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) ...
随机推荐
- PerfView专题 (第十二篇):对 C# 下的 SDK 类库进行监控(大结局)
一:背景 本篇是我们系列文章的最后一篇,前面的文章中大多是在 CLR Runtime 以及 OS 层面进行监控来发现各种可疑的程序问题,除了这两个层面,其实我们还可以对 SDK 中一些类进行洞察,比如 ...
- WebGPU实现Ray Packet
大家好~本文在如何用WebGPU流畅渲染百万级2D物体?基础上进行优化,使用WebGPU实现了Ray Packet,也就是将8*8=64条射线作为一个Packet一起去访问BVH的节点.这样做的好处是 ...
- docker存储管理及实例
一.Docker存储概念 1.容器本地存储与Docke存储驱动 容器本地存储:每个容器都被自动分配了内部存储,即容器本地存储.采用的是联合文件系统.通过存储驱动进行管理. 存储驱动:控制镜像和容器在 ...
- KingbaseES R6 集群创建流复制只读副本库案例
一.环境概述 [kingbase@node2 bin]$ ./ksql -U system test ksql (V8.0) Type "help" for help. test= ...
- spark 写入数据到Geomesa(Hbase)
package com.grady.geomesa import org.apache.spark.sql.jts.PointUDT import org.apache.spark.sql.types ...
- 接口测试神器Apifox,亲测好用!
自己关注的公众号比较多,之前有收到过有关 Apifox 的文章,自己也是大致看看,还没有用过它! 最近看到比较多有关 Apifox 的文章,所以自己就花了点时间去研究它,使用完后发现确实比Postma ...
- 四元数Quaternion的基本运算
技术背景 在前面一篇文章中我们介绍了欧拉角死锁问题的一些产生背景,还有基于四元数的求解方案.四元数这个概念虽然重要,但是很少会在通识教育课程中涉及到,更多的是一些图形学或者是工程学当中才会进行讲解.本 ...
- 使用logstash同步mysql 多表数据到ElasticSearch实践
参考样式即可,具体使用配置参数根据实际情况而定 input { jdbc { jdbc_connection_string => "jdbc:mysql://localhost/数据库 ...
- CAS核心思想、底层实现
★ 1.CAS 是什么 CAS 是比较并交换,是实现并发算法时常用到的一种技术.当内存的值和期望的值相等时,进行更新,否则 什么都不做 或 重来 . CAS 的底层实现:是靠硬件实现的,靠硬件的原子性 ...
- linux开机自启服务
前言:最近,有一个项目需要用到开机自动启动机房,所以就研究了一下 1.把node的快捷方式放在放在/usr/bin/(环境变量)下面,所有的命令默认是从这里面进行调用的 ln -s /home/too ...