如果不考虑L的范围,就是一道简单的dp题。f[i]表示走到位置i,踩到的石头个数的最小值,则f[i]=min(f[i], f[j] + flag[i]),其中flag[i]表示位置i是否是石子,j在[i-s, i-t]之间。
当L很大时,但是石子个数很少最多只有100个,所以存在两个石子之间有很大的空隙。
下面分两种情况来讨论:
- 当s==t:走法唯一,每次跳的距离为s,统计在位置满足是s的整数倍的上的石子的个数
- 当s!=t: 我们考虑不能被s, s+1, s+2, ..., T 所表示的数有哪些。结论:如果我们只用两个相邻的数p, q,那么不能被表示的数的最大值是(p-1)(q-1)-1,因此所有大于等于(p-1)(q-1)-1的数一定可以被p,q表示出来。
当p=9,q=10时取到最大值,此时(p-1)(q-1)-1=72,因此所有大于72的数,一定可以被s, s+1, s+2, ..., T 表示出来。所以将每个距离前一个石子距离超过100的石子都可以往左压缩,将线段的长度缩短为100,得到的结果是等价的。此时最多只会用到100*100=10000个位置,复杂度可以通过了
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200000;
int stones[110];
int f[N], flag[N];
int main() {
int L;
scanf("%d",&L);
int s,t,m;
scanf("%d %d %d", &s,&t,&m);
for(int i = 1; i <= m; i++){
scanf("%d", &stones[i]);
}
sort(stones, stones+m+1);
//当 s=t,每次跳只能跳固定的距离,所以统计 跳到的石子总和就是答案
if(s==t){
int sums = 0;
for(int i = 1; i<=m;i++){
if(stones[i]%s==0)
sums ++;
}
printf("%d", sums);
return 0;
}
//将 石子点往左压缩
for(int i = 1, last = 0, offset = 0; i<=m;i++){
//当 前后两个石子距离大于100,就将后面的石子往左移动一个offset,移动之后对结果无影响,因为距离太大,超过了一步能移动的最大距离。
if(stones[i]-last>=100){
offset += stones[i]-last-100;
}
last = stones[i];
stones[i] -= offset;
}
//标记石子位置
for(int i = 1; i <= m; i++){
flag[stones[i]] = 1;
}
//动态规划求解答案
//遍历所有可以跳到的位置,不是遍历 每个石子
L = stones[m]+10; // 最远跳到最后一个石子的右边最多10就可以了。不需要跳到或跳过终点,只要跳过了最后一个石头,那跳到最后,石子的数量也不会变多了。
for(int i = 1; i <= L; i++){
f[i] = 110; //最满足的情况的最大值,最多100个石子
for(int j = s; j<=t;j++)
{
if (i-j>=0)
f[i] = min(f[i], f[i-j]+flag[i]);
}
}
printf("%d",f[L]);
return 0;
}
// 64 位输出请用 printf("%lld")

京公网安备 11010502036488号