【笔记】入门DP(Ⅱ)
0X00 P1433 吃奶酪
状压
用
先预处理出点两两之间的距离,记为
转移方程:
注意,有前提条件,就是
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 石子合并(弱化版)
记
可以考虑将一个区间
可以写出转移方程:
其中
因为求最小值,所以初始化
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 数列
记
首先考虑直接删掉这个数,
其次,考虑不删这个数。当
最后答案是所有
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]雷涛的小猫
记
for(int j=1;j<=x;j++) scanf("%d",&y),a[i][h-y+1]++;
两种转移:
- 在同一颗树上下降
单位高度: - 从不同的树下来,下降
单位高度: 。有前提条件: 。
但这样时间复杂度
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 最大子段和
转移:
答案为
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 环状最大子段和(暂无例题)
分两种情况讨论。
- 子段的头尾在
~ 范围内:按上面做。 - 子段横跨了头尾,最大和即为数列总和减去头尾在
~ 范围内的最小子段和,求法相似。
记
记
初始化
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 双子序列最大和
对于每个
每个点左侧的最大子段和记
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 环状最大两段子段和
环状最大子段和 与 双子序列最大和 的结合体。
需要注意的是,这题中两个子段可以去相邻的,所以更麻烦一些。
- 首先,两子段的头尾在
~ 范围内,可以按原来做,只要最后求答案改成可以相邻即可:
for(int i=1;i<n;i++) mx=max(mx,f1[i]+f2[i+1]);
- 其次,两子段其中一段横跨了头尾,这时不用考虑相邻,因为相邻就会变成第一种情况。只要分类讨论,算一遍原数组和原数组最小子段和,整体后移一位的数组的最小子段和,取
与 作差即可。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!