P11013 「ALFR Round 4」C 粉碎 题解
P11013 「ALFR Round 4」C 粉碎
起初有 nnn 张牌,第 iii 张牌的点数为 AiA_iAi。
重复执行 nnn 轮以下操作:
先选择将第 iii 张牌置于牌堆的最左边或者最右边;
接着立即删除牌堆内相同点数的两个牌及之间的牌。
问最多能粉碎多少张牌。
1≤n≤5×1051\le n\le5\times10^51≤n≤5×105,1≤Ai≤n1\le A_i\le n1≤Ai≤n。
思路:
DP+贪心(?
模拟发现,每个牌有三种状态:一定被删,一定留下,留下或删除都可以。
设 fi,0/1f_{i,0/1}fi,0/1 表示第 iii 张牌删去/保留后,剩下的最小牌数。那我们可以推出 fi,0=0f_{i,0}=0fi,0=0,fi,1=min(fi−1,0,fi−1,1)f_{i,1}=\min (f_{i-1,0},f_{i-1,1})fi,1=min(fi−1,0,fi−1,1)。考虑什么时候会出现都可以的情况,只有当这个会导致删除的时候,此时放在左边或者右边不确定,这区间(0,i)内的数也就删除或保留不确定了。
不过直接一个个更新复杂度太高,我们发现每次更新的都是前缀,所以只需要记录最大值就可以了。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,a[N];
int T;
int num[N],lst[N];
int p[N],f[N][2];
int main(){scanf("%d",&T);while(T--){memset(num,0,sizeof num);memset(lst,0,sizeof lst);memset(p,0,sizeof p);memset(f,0x3f,sizeof f);scanf("%d",&n);for(int i=1;i<=n;i++) {scanf("%d",&a[i]); if(num[a[i]]==0){p[i]=1;}lst[i]=num[a[i]];num[a[i]]=i;}int mx=0;for(int i=1;i<=n;i++){int pre=lst[i];if(pre<=mx&&p[pre]==1) p[pre]=2;if(p[pre]==0){//删去 p[i]=1;f[i][1]=min(f[i-1][1],f[i-1][0])+1;}else if(p[pre]==1){p[i]=0;f[i][0]=0;mx=i;}else if(p[pre]==2){p[i]=2;f[i][1]=min(f[i-1][1],f[i-1][0])+1;f[i][0]=0;mx=i;}}printf("%d\n",max(0,n-min(f[n][0],f[n][1])));}return 0;
}