过河
Description
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
Analysis
动规方程非常好想,对于一个点只能由其前方一点跳s~t步得来,所以dp[i]=min(dp[i-t...i-s])+stone[i]。可是看一眼数据,L<=10^9,数组开不出来,怎么办呢?突破口是M<=100,既然石子数非常的少,那么中间有一大堆的白地进行了冗余的计算,怎么跳过白地的计算呢?不难想到,对于s!=t时,能跳的距离中一定既有奇数又有偶数,经过漫长的跳跃,一定能够跳到任何点,即对于较大的石头距离,一定不会踩到石头。那么需要处理的只有距离较近的石头,如果把大距离的石头直接删去是不行的,因为那样会让跳短距离的时候失去太多可能性,所以把大距离赋为绝对可以跳过,又不妨碍计算的值,100即可。
Code
#include <bits/stdc++.h>
int l,s,t,m,p[110],treat[110],stone[20110],dp[20110];
void special_treat(){
int ans=0;
for(int i=1;i<=m;i++)
if(p[i]%s==0)ans++;
std::cout<<ans<<std::endl;
}
int main(){
freopen("river.in","r",stdin);
freopen("river.ans","w",stdout);
std::cin>>l>>s>>t>>m;
for(int i=1;i<=m;i++)
std::cin>>p[i];
if(s==t){
special_treat();
return 0;
}
p[++m]=l;
std::sort(p+1,p+m+1);
for(int i=1;i<=m;i++)
if(p[i]-p[i-1]>=200)
treat[i]-=p[i]-p[i-1]-200;
int sum=0;
for(int i=1;i<=m;i++){
sum+=treat[i];
p[i]+=sum;
}
l=p[m--];
for(int i=1;i<=m;i++)
stone[p[i]]=1;
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=s;i<=l+t-1;i++)
for(int j=s;j<=t;j++)
dp[i]=std::min(dp[i],dp[i-j]+stone[i]);
int ans=0x3f3f3f3f;
for(int i=l;i<=l+t-1;i++)
ans=std::min(ans,dp[i]);
std::cout<<ans<<std::endl;
return 0;
}