题意:有n本顺序排放的书,第i本书的颜色是 a i a_i ai.你的目标是让所有颜色相同的书排在一起.你每次可以把一本书放到最右边,问最少需要操作多少次.
首先令dp[i]状态为[i,n]且满足条件的最大可不动元素个数,那么答案就为n-dp[1];
显然dp[n]=1,从后往前转移.
有两种情况:
1.不保留当前元素,那么dp[i]=dp[i+1]
2.保留当前元素,那么显然从当前a[i]到最后一个(最右边)a[i]出现的位置之间的所有元素都不能保留,那么最后一个之后的呢?显然可以使得dp[i]=dp[j+1]+cnt(j为最右边的a[i]的位置,cnt为这两者之间的所有a[i]的计数)
注意这种情况只有在当前a[i]为最左边的a[i]时才行,否则dp[i]=cnt[i].
那么为什么呢?cf的题解似乎没有解释清楚这一点.
是因为虽然说是最大保留作为dp状态,但这个dp是有条件的,即保留下来的最右部分可以去除一些数,而不是最右边的部分必须是完整的(即只有完整的非末端才能从右边转移过来的dp继承下去,而任意元素都可以作为末端)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 600000
int a[MAXN];
ll mod=1e9+7;
ll n,m;
int pos[MAXN];
int nxt[MAXN];
int dp[MAXN];
int r[MAXN];
int cnt[MAXN];
int c[MAXN];
int l[MAXN];
int main()
{
//freopen("./tt.txt","r",stdin);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=n;i>=1;i--)
{
nxt[i]=pos[a[i]];
pos[a[i]]=i;
cnt[i]=++c[a[i]];
r[a[i]]=max(i,r[a[i]]);
l[a[i]]=i;
}
for(int i=n;i>=1;i--)
{
dp[i]=dp[i+1];
if(i==l[a[i]])
dp[i]=max(dp[i],dp[r[a[i]]+1]+cnt[i]);
else
dp[i]=max(dp[i],cnt[i]);
}
cout<<n-dp[1]<<endl;
}
更接近准确思路的递推如下:
(其余部分相同)
for(int i=n;i>=1;i--)
{
dp[i]=dp[i+1];
dp[i]=max(dp[i],cnt[i]);
if(i==l[a[i]])
dp[i]=max(dp[i],dp[r[a[i]]+1]+cnt[i]);
}
}