(P1052 [NOIP2005 提高组])过河(动态规划+裴蜀定理)

题目链接:[NOIP2005 提高组] 过河 - 洛谷

分析:其实很容易想到的就是设f[i]表示跳到坐标为i的位置时所踩到的最少石子数,那么显然有

f[i]=min(f[i-T~i-S])+vis[i],其中vis[i]标记坐标为i的位置是否有石子,但是当我们读完数据范围后发现这样是不行的,因为数据范围是1e9,显然会超时,但是发现石子数量不是很多,所以这个时候我们就需要从石子之间的距离这个角度来对本道题目来进行优化了。

假设我们每次可以走s~t步,如果s==t,那么我们只有一种走的方式,那么这样答案就很容易求出来,现在我们来分析一下s!=t的情况,那么我们每步可以走s步也可以走s+1,步,那么对于任意的w>=(s+1)*s,我们都可以通过每次走s或s+1步来到达,这个是由裴蜀定理推论得到的。

先来说一下裴蜀定理:对于任意的x和y都能够找到a和b使得a*x+b*y=gcd(x,y)成立

那么一定有k*a*x+k*b*y=k*gcd(x,y)成立

由于s和s+1互质(任意相邻两个数均互质),一定有a*s+b*(s+1)=w(w>=s*(s+1))成立,现在的问题就是我们能不能找到正整数解满足这个等式我们假设0<=a<=s,那么b=(w-a*s)/(s+1)>=0,所以一定存在正整数a和b满足上面方程,那也就是说任意大于s*(s+1)的数均可以由s和s+1来得到,那么当两个石子之间的距离大于s*(s+1)时,我们就可以把他们之间的距离变为s*(s+1),这样就可以按照一开始分析的思路来解决这道题目了。需要注意的就是当s等于t时我们无法走s+1步,这一种情况需要特判。

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=1e7+10;
int f[N],a[N],b[N];
bool vis[N];
int main()
{
	int L;
	cin>>L;
	int s,t,m;
	cin>>s>>t>>m;
	for(int i=1;i<=m;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+m+1);
	if(s==t)//特判s=t的情况 
	{
		int cnt=0;
		for(int i=1;i<=m;i++)
			if(a[i]%s==0) cnt++;
		printf("%d",cnt);
		return 0;
	}
	for(int i=1;i<=m;i++)
	{
		if(a[i]-a[i-1]>s*(s+1)) b[i]=b[i-1]+s*(s+1);
		else b[i]=b[i-1]+a[i]-a[i-1];
		vis[b[i]]=true;
	}
	memset(f,0x3f,sizeof f);
	f[0]=0;
	int ans=0x3f3f3f3f;
	L=b[m]+10;//最后一步跳的距离不肯能超过10 
	for(int i=s;i<=L;i++)
		for(int j=s;j<=min(t,i);j++)
			f[i]=min(f[i],f[i-j]+vis[i]);
	for(int i=b[m];i<=L;i++)
		ans=min(ans,f[i]);
	printf("%d",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值