Luogu P1052 过河(dp)
题意
题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:\(0,1,\dots ,L\)(其中\(L\)是桥的长度)。坐标为\(0\)的点表示桥的起点,坐标为\(L\)的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是\(S\)到\(T\)之间的任意正整数(包括\(S,T\))。当青蛙跳到或跳过坐标为\(L\)的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度\(L\),青蛙跳跃的距离范围\(S,T\),桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入输出格式
输入格式:
第一行有\(1\)个正整数\(L(1\leq L\leq 10^9)\),表示独木桥的长度。
第二行有\(3\)个正整数\(S,T,M\),分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,其中\(1\leq S\leq T\leq 10,1 \le M \le 100\)。
第三行有\(M\)个不同的正整数分别表示这\(M\)个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出格式:
一个整数,表示青蛙过河最少需要踩到的石子数。
输入输出样例
输入样例#1:
10
2 3 5
2 3 5 6 7
输出样例#1:
2
说明
对于\(30\%\)的数据,\(L\leq 10000\);
对于全部的数据,\(L\leq 10^9\)。
\(2005\)提高组第二题
思路
先考虑部分分的做法。我们定义\(dp[i]\)为跳到坐标\(i\)时所需踩的最少石头数,那么\(dp[i]\)可以从\(dp[i-t],dp[i-t+1],\dots ,dp[i-s]\)转移过来。
但是当\(L\leq 10^9\)时,数组显然已经开不下了,就算空间够,时间上也不能完成。
想象这么一段区间,它以石头开头,以石头结尾,而且它十分的长,我们会发现,这一段区间中的大部分\(dp\)值相同,因为中间是没有石头踩的。所以我们可以把这么长的一段区间压缩成很少数目的点,然后直接\(dp\)就好了。
不过还有一个特例:当\(s=t\)时,不能用上述方法,我们就特判之后数学方法处理就好了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=50005,B=100;
int l,s,t,m,last,ans=INT_MAX,dp[MAXN],a[105],p[MAXN],hjj[MAXN];
int read()
{
int re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
int main()
{
l=read(),s=read(),t=read(),m=read();
for(int i=1;i<=m;i++) a[i]=read();
if(s==t)
{
ans=0;
for(int i=1;i<=m;i++) if(a[i]%s==0) ans++;
printf("%d",ans);
return 0;
}
a[0]=0,a[m+1]=l,m+=2;
sort(a,a+m);
for(int i=1;i<m;i++)
if(a[i]-a[i-1]<=B) last+=a[i]-a[i-1],hjj[last]=1;
else last+=B,hjj[last]=1;
hjj[last]=0;
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(int i=0;i<=last;i++)
for(int j=s;j<=t;j++)
dp[i+j]=min(dp[i+j],dp[i]+hjj[i+j]);
for(int i=0;i<=t;i++) ans=min(ans,dp[last+i]);
printf("%d",ans);
return 0;
}