过河

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;
}

原文地址:https://www.cnblogs.com/qswx/p/9492497.html