分析
这道题其实是两个问,逐一分析。
第一问只需要纯贪心就可以了,往后扫一遍,看看到哪里必须要分了,答案加一就行。
第二问一开始设的是 f [ i ] [ j ] f[i][j] f[i][j] 为前 i i i 个人分 j j j 段的最小危险值,发现转移的时候多划分段时没法判断是否+1,所以这个思路就夭折了。
因为肯定要对每个区间进行转移,所以可以预处理出区间 [ i , j ] [i,j] [i,j] 的宗|教种类数,这个是 n 2 n^2 n2 的时间。然后设 f [ i ] f[i] f[i] 为前 i i i 个人的最小危险值。
然后我们枚举分界点,如果 [ j , i ] [j,i] [j,i] 这个区间内的种类数量符合要求,那么就可以转移,分界点就在 j − 1 j-1 j−1 这个地方。转移方程呼之欲出: f [ i ] = m i n ( f [ i ] , f [ j − 1 ] + s [ j ] [ i ] ) f[i]=min(f[i],f[j-1]+s[j][i]) f[i]=min(f[i],f[j−1]+s[j][i])
答案显然为 f [ n ] f[n] f[n]。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,k,a[1010],f[1010],s[1010][1010];
int ans,tot,cnt=1,v[30];
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(!v[a[i]]) tot++;
v[a[i]]=1;
if(tot>k)
{
cnt++,tot=1;
for(int j=1;j<=20;j++) v[j]=0;
v[a[i]]=1;
}
}
cout<<cnt<<endl;
for(int i=1;i<=n;i++)
{
memset(v,0,sizeof(v));
tot=0;
for(int j=i;j<=n;j++)
{
if(!v[a[j]]) tot++;
v[a[j]]=1;
s[i][j]=tot;
}
}
memset(f,0x3f,sizeof(f));
f[0]=0;f[1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)//枚举分界点
{
if(s[j][i]>k) continue;
f[i]=min(f[i],f[j-1]+s[j][i]);
}
// cout<<f[i]<<' ';
}
cout<<f[n];
return 0;
}
上次文章被和谐的我,瑟瑟发抖。