题意
相比于easy version唯一不同的地方在于k的非0,也就是你可以在这n个数任意选择k个数,变成其他的数字,求出最少的区间数。
思路
这道题的dp还真的挺难的,当时就觉得easy version出来了,hard version搏一搏hhhc
这道题用
d
p
[
i
]
[
j
]
代
表
前
i
个
数
使
用
j
次
改
变
数
值
的
权
利
的
区
间
最
小
数
dp[i][j]代表前i个数使用j次改变数值的权利的区间最小数
dp[i][j]代表前i个数使用j次改变数值的权利的区间最小数
那么状态转移方程就是
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
l
[
i
]
[
x
]
−
1
]
[
j
−
x
]
+
1
)
dp[i][j]=min(dp[i][j],dp[l[i][x]-1][j-x]+1)
dp[i][j]=min(dp[i][j],dp[l[i][x]−1][j−x]+1)
l
[
x
]
[
y
]
代
表
的
是
上
个
划
分
点
倒
x
经
历
了
y
次
修
改
l[x][y]代表的是上个划分点倒x经历了y次修改
l[x][y]代表的是上个划分点倒x经历了y次修改
l数组可以用双指针算法预处理出来
#include<iostream>
#include<cstring>
#include<map>
#include<cmath>
using namespace std;
const int N=1e7+9;
const int NN=1e7+4;
const int M=2e5+10;
int primes[NN], cnt; // primes[]存储所有素数
bool st[NN]; // st[x]存储x是否被筛掉
int a[10000010];
int dp[M][21];
int l[M][21];
int mp[20000110];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int bym(int n){
int res=1;
for(int i=0;primes[i]<=n/primes[i];i++){
int u=0;
while(n%primes[i]==0){
n/=primes[i];
u++;
}
if(u%2)res*=primes[i];
}
if(n!=1)res*=n;
return res;
}
int main(){
int T;
cin>>T;
get_primes(NN);
while(T--){
//map<int,int>mp;
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
int s;
scanf("%d",&s);
a[i]=bym(s);
//mp[a[i]]=0;
}
for(int p=0;p<=k;p++){
int cnt=0;
//map<int,int>mp;
for(int i=1;i<=n;i++)mp[a[i]]=0;
for(int i=1,j=1;i<=n;i++){
mp[a[i]]++;
if(mp[a[i]]>1)cnt++;
while(cnt>p){
if(mp[a[j]]>1)cnt--;
mp[a[j]]--;j++;
}
l[i][p]=j;
}
//memset(mp,0,sizeof mp);
}
//memset(dp,0x3f,sizeof dp);
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
dp[i][j]=0x3f3f3f3f;
for(int p=0;p<=j;p++)
dp[i][j]=min(dp[i][j],dp[l[i][p]-1][j-p]+1);
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<=k;i++)ans=min(ans,dp[n][i]);
cout<<ans<<endl;
}
}
建议多看看,要不然还是不会做