c语言动态规划坐标,C语言动态规划(七)_过河(Vijos P1002)

C语言动态规划(7)___过河(Vijos P1002)

Problem Description

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

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

对于30%的数据,L <= 10000;

对于全部的数据,L <= 10^9。

Input

输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1

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

Output

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

Sample Input

10

2 3 5

2 3 5 6 7

Sample Output

2

本来这道题可以说是一道简单的DP题,状态转移方程也容易找出:f [ i ] = min ( f [ i - j ] )  +  stone [ i ]  S <= j <=T && i >= j

但是,这道题的难点就是独木桥的长度L最高可达到10^9.如果用常规方法用for循环从1递推到L + T,难免会超时.

这个时候我们要用离散化思想来压缩路径.

我们会发现:

①f [ i ]只跟f [ (i - j)min ]~f

[ (i - j)max ]   (S <= j <= T && i >= j)有关.

②石头的个数M远远小于L.换句话意思就是在某两个石头之间存在一大段空白,这个时候f

[ i ]在这个区域递推值都是不变的.

由于上面的①和②我们可知在递推f

[ step ]的时候,我们最多需要f

[ step - T ]~ f [ step - S ]之间的值.这个时候我们只要第一次推出某一个 step1位置,使得:

f

[ step1 - T ]~f [ step1 - S ]与f

[ step - T ]~f [ step - S ]每个值对应相等.(如果S≠T,f [

step1 - T ]~f [ step1 - S ]每个值也是相等的)

剩下的从 step1到step就不用递推了.这里我们就达到了优化的左右,路径长度从

step 优化到 step1.

现在我们只需要找到最大的 step1,然后就可以把 L 压缩到

step1*M.

若p*x+(p+1)*y=Q(采用跳跃距离

p 和 p+1 时可以跳至任何位置 Q),则在Q ≥ P*(P-1)时是一定有解的。

由于题目给出的一个区间是1≤S≤T≤10,于是当相邻的两个石子之间的距离不小于8*9=72时,则后面的距离都可以到达,我们就可以认为它们之间的距离就是72。如此一来,我们就将原题L的范围缩小为了100*72=7200,动态规划算法完全可以承受了。

特殊的,S=T的时候,那么上式方程无恒解,而f

[ step1 - T ]~f [ step1 - S ]之间的每个值并不是都想等的,但f

[ step1 - T ]~ f [ step1 - S ]与f

[ step - T ]~f [ step - S ]每个值对应相等.所以对于这种情况我们不能简简单单的把两个石头之间的距离压缩成72.而是还要加上除以72的余数.

压缩策略: 那么对于每两个石头之间的距离

Xi.

① 当Xi<2*72的时候,不予压缩.

②当Xi≥2*72的时候,压缩Xi=72-(Xi)%72 ;

总规模最大为:2*100*72=14400.

#include

#include

#include

using namespace std;

int f[3000005];

int stone[105];

bool vis[3000005];

int main()

{

int L,S,T,M;

int X[105];

scanf("%d%d%d%d",&L,&S,&T,&M);

int i,j,ans=0;

for(i=1;i<=M;i++)

scanf("%d",&stone[i]);

if(S==T)

{

for(i=1;i<=M;i++)

if(stone[i]%S==0)

ans++;

printf("%d\n",ans);

return 0;

}

stone[0]=0;

sort(stone+1,stone+M+1);

stone[M+1]=L;

for(i=1;i<=M;i++)

X[i]=stone[i+1]-stone[i];

for(i=1;i<=M;++i)

{

if((stone[i+1]-stone[i])>72)

{

if(X[i]>72)

stone[i+1]=stone[i]+(stone[i+1]-stone[i])%72+72;

else

stone[i+1]=stone[i]+(stone[i+1]-stone[i])%72;

}

}

L=stone[M+1];

for(i=1;i<=M;++i)

vis[stone[i]]=1;

f[0]=0;

for(i=1;i<=L+T;++i)

{

f[i]=M;

for(j=S;j<=T;++j)

if(i>=j && f[i]>f[i-j]+vis[i])

f[i]=f[i-j]+vis[i];

}

ans=M;

for(i=L;i<=L+T;++i)

if(ans>f[i])

ans=f[i];

printf("%d\n",ans);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值