动态规划杂题(2200-2500)
有两个长度为
区间
区间
给定
思路点拨
我们考虑一个
-
不选择
, 那么 。 -
选择
,枚举 所属这一段的长度, 。
考虑优化,这里需要运用一种动态规划中常用的技巧:拆分绝对值。
我们将转移分为四类讨论:
Q:这样难道就不需要考虑
A:并不会,因为绝对值有性质:
对于上述的转移,我们将包含
因为
for(int i=1;i<=n;i++)
for(int j=1;j<=min(i,k);j++){
dp[i][j]=dp[i-1][j];
f[i-j][0]=max(f[i-j][0],dp[i-1][j-1]+a[i]+b[i]);
f[i-j][1]=max(f[i-j][1],dp[i-1][j-1]-a[i]+b[i]);
f[i-j][2]=max(f[i-j][2],dp[i-1][j-1]+a[i]-b[i]);
f[i-j][3]=max(f[i-j][3],dp[i-1][j-1]-a[i]-b[i]);
dp[i][j]=max(dp[i][j],f[i-j][0]-a[i]-b[i]);
dp[i][j]=max(dp[i][j],f[i-j][1]-a[i]+b[i]);
dp[i][j]=max(dp[i][j],f[i-j][2]+a[i]-b[i]);
dp[i][j]=max(dp[i][j],f[i-j][3]+a[i]+b[i]);
}
现在有一个长度为
思路点拨
发现产生贡献的两端
-
表示考虑到前缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到前缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到后缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到后缀 ,修改了 次之后最长 可以是多少。
如果可以求出来,最后通过一些手段合并答案就可以了。
转移也比较简单:
我们可以对上述四个数组做二维前缀最大值,数组的意义将转变为:
-
表示考虑到前缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到前缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到后缀 ,修改了 次之后最长 可以是多少。 -
表示考虑到后缀 ,修改了 次之后最长 可以是多少。
现在我们如果枚举一个常数
定义
可以看见,转移的时间等于
每一次我们枚举一个常数
for(int i=1;i<=n;i++){
pre0[i][0]=(a[i]==1)?0:pre0[i-1][0]+1;
for(int j=1;j<=min(i,k);j++)
pre0[i][j]=(a[i]==1)?pre0[i-1][j-1]+1:pre0[i-1][j]+1;
pre1[i][0]=(a[i]==0)?0:pre1[i-1][0]+1;
for(int j=1;j<=min(i,k);j++)
pre1[i][j]=(a[i]==0)?pre1[i-1][j-1]+1:pre1[i-1][j]+1;
}
for(int i=n;i;i--){
suc0[i][0]=(a[i]==1)?0:suc0[i+1][0]+1;
for(int j=1;j<=min(n-i+1,k);j++)
suc0[i][j]=(a[i]==1)?suc0[i+1][j-1]+1:suc0[i+1][j]+1;
suc1[i][0]=(a[i]==0)?0:suc1[i+1][0]+1;
for(int j=1;j<=min(n-i+1,k);j++)
suc1[i][j]=(a[i]==0)?suc1[i+1][j-1]+1:suc1[i+1][j]+1;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
pre0[i][j]=max(pre0[i][j],pre0[i-1][j]);
pre1[i][j]=max(pre1[i][j],pre1[i-1][j]);
}
}
for(int i=n;i;i--){
for(int j=0;j<=k;j++){
suc0[i][j]=max(suc0[i][j],suc0[i+1][j]);
suc1[i][j]=max(suc1[i][j],suc1[i+1][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
pre0[i][j]=max(pre0[i][j],pre0[i][j-1]);
pre1[i][j]=max(pre1[i][j],pre1[i][j-1]);
suc0[i][j]=max(suc0[i][j],suc0[i][j-1]);
suc1[i][j]=max(suc1[i][j],suc1[i][j-1]);
}
}
memset(mx,-1,sizeof(mx));
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++)
mx[pre0[i][j]]=max(mx[pre0[i][j]],suc1[i+1][k-j]);
for(int j=0;j<=k;j++)
mx[suc0[i][j]]=max(mx[suc0[i][j]],pre1[i-1][k-j]);
}
mx[0]=max(mx[0],pre1[n][k]);
for(int i=1;i<=n;i++){
int ans=0;
for(int j=0;j<=n;j++)
if(mx[j]!=-1) ans=max(ans,j*i+mx[j]);
cout<<ans<<" ";
}
给定大小为
每一秒进行如下操作:
- 从
中等概率随机选择一个数 。 - 将
从 中删去。 - 若
且 ,则将 加入 。
求
思路点拨
如果去除第三点限制中的
我们定义状态
这里讲一下为什么只乘上
现在的时间复杂度是
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!