CF1922F 题解
感觉大部分(也许是所有)题解都没说到点上,于是我来发一篇。
思路
结论:存在一种最优解使得区间之间没有相交且不包含的关系。
证明:考虑将连续相同的数缩成一个点,那么操作一次相当于将这个区间缩成一个点,如果能证明这样操作答案不变,就证明了结论(不会有区间与一个长度为
因此操作的区间形成了树形结构,可以使用区间 dp。设
这个转移方程的意思是,如果当前区间不操作,那么它一定需要分成两半,并分别操作。否则,当前区间会变成一个不为
这个转移方程和上面的类似,如果当前区间不操作,则它需要分成两半分别操作。否则,当前区间操作的前提是区间所有数都不为
在第一个转移方程中存在互相转移的情况。但是可以发现只有 dp 值最小的才会更新别人,而它不会被别人更新,所以直接先求出来第一部分的 dp 值,再求出最小值,用它加一更新别人。
这样暴力做的复杂度是
发现对于一个左端点,使用特定次数操作使得满足条件的右端点存在单调性,所以考虑交换下标和值。设
代码
暴力的代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[110];
int dp[110][110][110];
int ans[110][110][110];
int main()
{
int t; cin>>t; while(t--)
{
cin>>n>>m;
for(int i=1; i<=n; ++i) cin>>a[i];
for(int len=1; len<=n; ++len)
{
for(int i=1; i<=n-len+1; ++i)
{
int j=i+len-1;
for(int k=1; k<=m; ++k)
{
bool flag=1;
for(int l=i; l<=j; ++l)
{
if(a[l]==k) { flag=0; break; }
}
if(flag) { dp[i][j][k]=0; continue; }
dp[i][j][k]=1e9;
for(int l=i; l<j; ++l) dp[i][j][k]=min(dp[i][j][k],dp[i][l][k]+dp[l+1][j][k]);
}
for(int k=1; k<=m; ++k)
{
bool flag=1;
for(int l=i; l<=j; ++l)
{
if(a[l]!=k) { flag=0; break; }
}
if(flag) { ans[i][j][k]=0; continue; }
ans[i][j][k]=1e9;
for(int l=i; l<j; ++l) ans[i][j][k]=min(ans[i][j][k],ans[i][l][k]+ans[l+1][j][k]);
}
int in=1e9;
for(int k=1; k<=m; ++k) in=min(in,dp[i][j][k]);
for(int k=1; k<=m; ++k) dp[i][j][k]=min(dp[i][j][k],in+1),ans[i][j][k]=min(ans[i][j][k],dp[i][j][k]+1);
}
}
int aans=1e9;
for(int i=1; i<=m; ++i) aans=min(aans,ans[1][n][i]);
cout<<aans<<'\n';
}
return 0;
}
优化后的代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[110];
int dp[110][110][110];
int ans[110][110][110];
int lst[110][110],llst[110][110];
int main()
{
int t; cin>>t; while(t--)
{
cin>>n>>m;
for(int i=1; i<=n; ++i) cin>>a[i];
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
int lim=n/m+1,aans=1e9;
for(int i=1; i<=m; ++i) lst[n+1][i]=llst[n+1][i]=n;
for(int i=n; i>=1; --i)
{
for(int j=1; j<=m; ++j)
{
if(a[i]==j) lst[i][j]=i-1,llst[i][j]=llst[i+1][j];
else lst[i][j]=lst[i+1][j],llst[i][j]=i-1;
}
for(int j=0; j<=lim; ++j)
{
for(int k=1; k<=m; ++k)
{
if(j==0) { dp[i][j][k]=lst[i][k],ans[i][j][k]=llst[i][k]; continue; }
for(int l=0; l<j; ++l)
{
if(dp[i][l][k]==n) dp[i][j][k]=n;
else dp[i][j][k]=max(dp[i][j][k],dp[dp[i][l][k]+1][j-l][k]);
if(ans[i][l][k]==n) ans[i][j][k]=n;
else ans[i][j][k]=max(ans[i][j][k],ans[ans[i][l][k]+1][j-l][k]);
}
}
if(j!=0)
{
int ax=0;
for(int k=1; k<=m; ++k) ax=max(ax,dp[i][j-1][k]);
for(int k=1; k<=m; ++k)
{
dp[i][j][k]=max(dp[i][j][k],ax),ans[i][j][k]=max(ans[i][j][k],dp[i][j-1][k]);
dp[i][j][k]=lst[dp[i][j][k]+1][k];
ans[i][j][k]=llst[ans[i][j][k]+1][k];
}
}
if(i==1)
{
for(int k=1; k<=m; ++k) if(ans[i][j][k]==n) aans=min(aans,j);
}
}
}
cout<<aans<<'\n';
}
return 0;
}