这道题不是一道状压DP。
当然,如果您想用我的方法写题,
建议您先去看看Luogu P3951 小凯的疑惑【数论】,这道题是前置知识。
为什么呢?
首先看这题的数据范围
明显直接DP会炸。
我们在小凯的疑惑里可得,最大不能到的价值为 a b − a − b ab-a-b ab−a−b,后面的都能得到。
那我们不妨照这题的思路,将它带入到这题里。
当奇数和偶数都被枚举到时,后面所有情况都会被枚举到,
那么我们考虑最大能枚举到奇数和偶数的位置,一番推理 可得,这个位置是 T ∗ ( T − 1 ) T*(T-1) T∗(T−1),这样我们就可以压缩空间和运算次数。
for(int i=1; i<=m; i++)
{
y[i]=min(a[i]-a[i-1],90); //10*(10-1)=90
l+=y[i]; //重新统计长度
flag[l]=1; //标记石头的位置
}
然后我们开始DP。
设 f [ i ] f[i] f[i] 表示当前第 i i i 个点走过了 x x x 个石头。
可得动态转移方程为:
f [ i ] = min ( f [ i ] , f [ i − j ] + f l a g [ i ] ) ; f[i]=\min(f[i],f[i-j]+flag[i]); f[i]=min(f[i],f[i−j]+flag[i]);
flag存的是当前点的有没有石头。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int a[1001],y[1001],flag[9000001],f[9000001];
int l,s,t,m,js;
int main()
{
cin>>l>>s>>t>>m;
for(int i=1; i<=m; i++)
{
scanf("%d",&a[i]);
if(a[i]%s==0)
js++;
}
if(s==t)
{
cout<<js;
return 0;
}
sort(a+1,a+1+m);
y[m+1]=min(l-a[m],90);
l=0;
for(int i=1; i<=m; i++)
{
y[i]=min(a[i]-a[i-1],90);
l+=y[i];
flag[l]=1;
}
l+=y[m+1];
for(int i=1; i<=l+9; i++) //可能会转移到 l 后面的状态,所以要到 l+9
{
f[i]=100100101;
for(int j=s; j<=t; j++)
if(i>=j)
f[i]=min(f[i],f[i-j]+flag[i]);
}
int ans=100010010;
for(int i=l; i<=l+9; i++)
ans=min(ans,f[i]);
cout<<ans;
return 0;
}