[vijos1002][NOIP2005]过河
Description
给定一条数轴,起点为\(0\),数轴的某些整数点上有石子。每次可以移动的区间为\([S,T]\)。求当到达或超过\(L\)时,最少踩到的石子数。
Input
输入的第一行有一个正整数\(L(1\;\leq\;L\;\leq\;10^9)\)。
第二行有三个正整数\(S,T,M,M\)表示桥上石子的个数,其中\(1\;\leq\;S\;\leq\;T\;\leq\;10,1\;\leq\;M\;\leq\;100\)。
第三行有\(M\)个不同的正整数分别表示这\(M\)个石子在数轴上的位置(数据保证桥的\(0\)和\(L\)处没有石子)。所有相邻的整数之间用一个空格隔开。
Output
输出只包括一个整数,表示最少踩到的石子数。
Sample Input
10
2 3 5
2 3 5 6 7
Sample Output
2
Solution
首先列出\(dp\)方程,\(f[i]\)表示到达\(i\)时最少踩到的石子数,则\(f[i]=min(f[i-k])(S\;\leq\;k\;\leq\;T)\)。
然后发现数据范围是\(10^9\),所以需要状压一下。
经过思考可以发现,当\(S<T\)时,如果在\(k+\small{S\;\times\;T}\)和\(k+\small{S\;\times\;T}+x(k\;\geq\;0,x>0)\)处有石子,则若存在一种方案可以越过\(k+\small{S\;\times\;T}\),那么也能越过\(k+\small{S\;\times\;T}+x\)。
具体证明时把\(\small{S\;\times\;T}\)看成\(\small{T}\)个\(\small{S}\)相加,易证能到达\(k+\small{S\;\times\;T}\),也能到达\(k+\small{S\;\times\;T}+x\)。
所以只需将距离超过\(\small{S\;\times\;T}\)的石子距离缩到\(\small{S\;\times\;T}\)就可以了。
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define M 101
#define L 20001
using namespace std;
int a[M],f[L],l,m,s,t;
bool sto[L];
inline void init(){
scanf("%d%d%d%d",&l,&s,&t,&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+m);
if(s==t){
for(int i=1;i<=m;i++)
if(!(a[i]%s)&&a[i]<=l)
f[0]++;
printf("%d\n",f[0]);
return;
}
for(int i=1,d;i<=m;i++){
if(a[i]-a[i-1]>s*t){
d=a[i]-a[i-1]-s*t;
for(int j=i;j<=m;j++)
a[j]-=d;
}
sto[a[i]]=true;
}
if(l-a[m]>s*t) l=a[m]+s*t;
fill(f+1,f+1+l,M);
for(int i=s;i<=l;i++){
f[i]=f[i-s];
for(int j=min(t,i);j>=s;j--)
f[i]=min(f[i],f[i-j]);
f[i]+=sto[i];
}
printf("%d\n",f[l]);
}
int main(){
freopen("river.in","r",stdin);
freopen("river.out","w",stdout);
init();
fclose(stdin);
fclose(stdout);
return 0;
}