洛谷1052——过河(DP+状态压缩)

题目描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:$0,1,…,L$(其中$L$是桥的长度)。坐标为$0$的点表示桥的起点,坐标为$L$的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是$S$到$T$之间的任意正整数(包括$S,T$)。当青蛙跳到或跳过坐标为$L$的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度$L$,青蛙跳跃的距离范围$S,T$,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入输出格式

输入格式:

第一行有$1$个正整数$L(1 \le L \le 10^9)$,表示独木桥的长度。

第二行有$3$个正整数$S,T,M$,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,其中$1 \le S \le T \le 10$,$1 \le M \le 100​$。

第三行有$M$个不同的正整数分别表示这$M$个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

输出格式:

一个整数,表示青蛙过河最少需要踩到的石子数。

输入输出样例

输入样例#1:

1
2
3
10
2 3 5
2 3 5 6 7

输出样例#1:

1
2

说明

对于30%的数据,$L \le 10000$;

对于全部的数据,$L \le 10^9$。

2005提高组第二题

思路

如果不考虑数据范围的话,可以很快得出递推关系式:$dp[i]=min(dp[i+k]+a[i]) (S\leq k \leq T)​$($a[i]​$为$i​$点的石头数$dp[i]​$表示到达$i​$点踩到的最少石头数)

然鹅看了数据范围后,时间和空间都是过不去的。所以需要选择别的方法:

  • 当$S=T$的时候,可以很容易得到:所有在$S$倍数位置上的点,都会走到,所以对该种情况进行特判

  • 我们可以发现石子在桥上放置的是非常稀疏的,而且当点的位置超过一定范围,点都是可以跳到的。所以可以将石子所在的位置压缩到这个范围里去。将压缩后位置储存起来作为新的石头的位置,按照这个位置进行dp即可

假设每次走$p$或者$p+1$步.我们知道$gcd(p,p+1)=1$.
由扩展欧几里得可知,对于二元一次方程组:
$px+(p+1)y=gcd(p,p+1)$是有整数解的,即可得:$px+(p+1)y=s$是一定有整数解的。
设$px+(p+1)y=s$的解为:$x=x_0+(p+1)t,y=y_0−pt$。令$0\leq x\leq p$(通过增减$t$个$p+1$来实现),$s>p\times (p+1)−1$,
则有:$y=\dfrac {s-px}{p+1}\geq \dfrac {s-p^{2}}{p+1} >\dfrac {p\left( p+1\right) -1-px}{p+1}\geq 0$
即表示,当$s\leq p\times (p+1)$时,$px+(p+1)y=s$有两个非负整数解,每次走$p$步或者$p+1$步,$p\times (p+1)$之后的地方均能够到达。
如果两个石子之间的距离大于$p\times (p+1)$,那么就可以直接将他们之间的距离更改为$p \times (p+1)$。
综上,得到压缩路径的方法:若两个石子之间的距离大于$t\times(t−1)$,则将他们的距离更改为$t\times (t−1)$。

因$为t\leq10$,因此我们可以直接将大于$10\times9$的距离直接化为$90​$.

关于路径压缩常数的选择,证明过程详见:https://blog.csdn.net/qq_34940287/article/details/77494073

AC代码

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
52
53
54
55
56
57
58
/*************************************************************************

> Author: WZY
> School: HPU
> Created Time: 2019-02-03 15:55:49

************************************************************************/
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define ms(a,b) memset(a,b,sizeof(a))
#define INF 0x7f7f7f7f
const int maxn=1e6+10;
const int mod=1e9+7;
using namespace std;
int a[maxn];
int dp[maxn];
int vis[maxn];
int main(int argc, char const *argv[])
{
ios::sync_with_stdio(false);
cin.tie(0);
int L;
int ans=0;
int s,t,m;
cin>>L;
cin>>s>>t>>m;
for(int i=1;i<=m;i++)
cin>>a[i];
if(s==t)
{
for(int i=1;i<=m;i++)
{
if(a[i]%s==0)
ans++;
}
cout<<ans<<endl;
return 0;
}
sort(a+1,a+1+m);
int _=90;
int res=a[1]%_;
vis[res]=1;
// 缩点
for(int i=2;i<=m;i++)
{
res+=(a[i]-a[i-1])%_;
vis[res]=1;
}
for(int i=res;i>=0;i--)
{
dp[i]=INF;
for(int j=s;j<=t;j++)
dp[i]=min(dp[i],dp[i+j]+vis[i]);
}
cout<<dp[0]<<endl;
return 0;
}

本文标题:洛谷1052——过河(DP+状态压缩)

文章作者:执念

发布时间:2019年02月03日 - 18:02

最后更新:2019年02月14日 - 17:02

原始链接:https://blog.wzy1999.wang/solve/luogu1052/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
0%