0%

[NOIP2005 提高组] 过河

我觉得这题在 luogu 上难度评级不正常,它应该不只是一个绿题...

做题的时候遇到的,感觉可以算是一种新的 dp 优化方式。

题目链接

题意

现在有一个独木桥,起点为 ,终点为 ,这个独木桥上一共有 个石头。

现在有一只青蛙在 的位置,它每一次跳跃可以使自己的位置增加 中的一个整数。

现在这只青蛙想要跳到 的位置,请问它最少需要踩到多少块石头。

题解

首先我们会发现,如果本题的 比较小,那么我们可以直接通过 dp 来解决。

方程为:

这样我们能拿到 分。

再次观察数据范围,我们发现 三个量都很小。

尤其是 这两个量,格外突出。

首先我们可以特判 的情况,这种情况我们可以直接算出答案。

对于剩下的情况,我们会发现,对于一个很大的距离 ,即使我们每一次只向前跳 的距离,我们也可以恰好“凑出”一个

这就告诉我们,假如两块石头之间的距离比较大,我们可以手动把它们之间的距离缩小。

那么具体缩小多少呢?

一种可行的思路每一次缩短 ,它是

还有一种思路就是 ,因为所有 的数字都可以使用任意相邻两个数“凑”出来。

时间复杂度是

我的代码中取的是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
using ll=long long;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
bool vis[30001];
int dp[30001];
int pre[102];
int a[102];
int l,s,t,n;
int main()
{
l=read(),s=read(),t=read(),n=read(),a[n+1]=l;
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+n+1);
if(s==t)
{
int ans=0;
for(int i=1;i<=n;i++) if(a[i]%s==0) ans++;
printf("%d\n",ans);
return 0;
}
for(int i=1;i<=n+1;i++)
{
pre[i]=a[i]-a[i-1];
if(pre[i]>=72) pre[i]=pre[i]%72+72;
}
for(int i=1;i<=n;i++) a[i]=a[i-1]+pre[i],vis[a[i]]=1;
l=a[n]+pre[n+1];
memset(dp,0x3f,sizeof(dp)),dp[0]=0;
for(int i=1;i<=l;i++) for(int j=s;j<=t&&j<=i;j++) dp[i]=min(dp[i],dp[i-j]+vis[i]);
int ans=0x3f3f3f3f;
for(int i=a[n];i<=l;i++) ans=min(ans,dp[i]);
printf("%d\n",ans);
return 0;
}

感谢观看!