专栏文章
题解:CF1987F2 Interesting Problem (Hard Version)
CF1987F2题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioemygo
- 此快照首次捕获于
- 2025/12/02 17:57 3 个月前
- 此快照最后确认于
- 2025/12/02 17:57 3 个月前
DaiRuiChen007无敌了!
唐氏区间 dp 拖了一年。
首先按照区间 dp 的基本思路:设 代表 区间的答案。考虑到我们要通过删除前面的数给删除后面的数机会,不妨设 代表删除完 在 要至少删除多少个数。
区间 dp 的两种常见转移方式,一种是 从 转移而来,另外一种是枚举中间断点,从 和 转移而来。
第一种转移在本题当中,可以看 这对数的删除转移情况,因为是先把 删完了,所以 在前面删的数的数量必须大于等于 。
第二种转移直接枚举断点,并排删除 和 ,注意删除 时候给 的删除带来的影响。
最后用 转移判断能删多少个数:如果 (要求 长度为偶)的值小于等于 ,就可以直接删除 转移而来。
时间复杂度 , 是什么做法呢?
CPP#include<bits/stdc++.h>
#define int long long
#define mp(a,b) make_pair(a,b)
using namespace std;
inline int read(){
int a=0,b=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') b=-1;
c=getchar();
}
while(isdigit(c)){
a=(a<<1)+(a<<3)+(c-'0');
c=getchar();
}
return a*b;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar(x%10+48);
}
inline void write1(int x){
write(x),putchar(' ');
}
inline void write2(int x){
write(x),putchar('\n');
}
int n;
int a[817];
int dp[817][817]; //dp[i][j] 代表消除完 [i,j] 至少要消除前面多少个数
int dp1[817]; //dp1[i] 代表最少保留前 i 个数的几个
//更新:最多删多少个
void solve(){
n=read();
for(int i=1;i<=800;i++){
for(int j=1;j<=800;j++){
dp[i][j]=1e18;
}
}
for(int i=1;i<=n;i++){
a[i]=read();
dp[i][i-1]=0;
dp1[i]=0;
}
for(int k=2;k<=n;k+=2){
for(int l=1,r=l+k-1;r<=n;l++,r++){
//现在转移的是 [l,r] 区间
if(l>=a[l]&&(l-a[l])%2==0){
int now=l-a[l];
if(dp[l+1][r-1]<=now) dp[l][r]=now;
}
for(int i=l+1;i<=r;i+=2){
//dp[l][i] 和 dp[i+1][r] 的连接
dp[l][r]=min(dp[l][r],max(dp[l][i],dp[i+1][r]-(i-l+1)));
}
// cout<<'#'<<l<<' '<<r<<' '<<dp[l][r]<<endl;
}
}
// putchar('\n');
dp1[0]=0;
for(int i=1;i<=n;i++){
dp1[i]=dp1[i-1];
// dp1[i]=dp1[i-1]+1; //第一种转移方案
for(int j=i-1;j>=1;j-=2){
if(dp1[j-1]>=dp[j][i]){
dp1[i]=max(dp1[i],dp1[j-1]+(i-j+1));
// cout<<'!'<<i<<' '<<j<<' '<<dp1[j-1]+(i-j+1)<<endl;
}
}
}
write2(dp1[n]/2);
return;
}
signed main(){
int T=read();
while(T--) solve();
return 0;
}//一定要挽回罗琪钧的遗憾啊,一定会挽回罗琪钧的遗憾的。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...