P1052 [NOIP2005 提高组] 过河(数学 + dp)

题目描述

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

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

输入格式

输入共三行,

  • 第一行有 1 个正整数 L,表示独木桥的长度。
  • 第二行有 3 个正整数 S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数。
  • 第三行有 M 个不同的正整数分别表示这 M 个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

输出格式

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

输入输出样例

输入 #1复制

10
2 3 5
2 3 5 6 7

输出 #1复制

2

说明/提示

【数据范围】

  • 对于 30% 的数据,1≤L≤104;
  • 对于 100% 的数据,1≤L≤109,1≤S≤T≤10,1≤M≤100。

【题目来源】

NOIP 2005 提高组第二题

解析:

看到这道题目,我就想到可以用单调队列进行解题。看了一下数据的范围。这方法好像不对劲。它的长度很长,但是石头很离散。这时有一道题启发起了我。这道题叫:小凯的疑惑 ,用 a,b面值的钱,钱的数量无限。求a,b拼凑后得不到最大的钱的值。这道题是一道小学奥数题ax + by = k (这里是x张a面值的钱 +y张b面值的钱),k不能达到的最大值,这个值后面的值都可以达到。

对ax + by = k 

对上面的式子同时mod(b)得 x = ma(mod b) (1 <= m <= b-1)

当满足上面式子 ax + by = k 时, y >= 0 

当要不满时,我们要取最大所以 y = -1时,因为越减以后越小。 ax - by = k 当k取最大,不满足这个ax + by = k这个式子。

所以a,b所表示不出最大的数为ab-a-b。后面的值都可以达到。

在这道题中就可以用到这个思想。

它的t 和s的范围是1~10,当t~s越小时,其最大的越小,这时它可以跳到后面任意一个点上。

所以我们要关注t = 10 s =10,最大的数最小,这个其限制时最大的。这里好好理解一下。

这里是极端的情况,应该是 t = 9,s = 10;后面看代码就懂了。

所以为什么两个相邻石头的距离大于80时,可以取 80(10 *10 - 10 -10)的原因 ,因为在这两个石头中范围很大可以随便跳过这个石头。

知道核心思路后就是起点到终点的长度可以缩小了。

在用简单的线性dp就可以解决了。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define endl '\n'
#define inf 1e15
const int maxn = 2e5+20;
int l,s,t,m,a[maxn],dp[maxn],f[maxn],d[maxn]; 
int main() {
	cin >> l;
	cin >> s>>t>>m;
	if(s==t)
	{
		int ans = 0;
		for(int i = 1;i <= m;i++) //跳的距离固定时 
		{
			int k;
			cin >> k;
			if(k%s==0)ans++;
		}
		cout << ans<<endl;
		return 0;
	}
	for(int i = 1;i <= m;i++){
		cin >> a[i];
	}
	sort(a+1,a+1+m);
	d[m+1] = min(l - a[m],80); //最后一块石头到终点的距离 
	int p = 0;
	for(int i = 1;i <= m;i++){
		d[i] = min(a[i] - a[i-1],80); p+=d[i];f[p] = 1; // p长度总长度 d[i]是第i块石头处理后的距离和前一块石头的距离  f[p]表示在p的距离有一块石头 
	}
	p+=d[m+1];int mi = 1e8;
	for(int i = 1;i<= p+10;i++){//遍历长度 O(p*(s - t)) 
			dp[i] = 1e8;
		for(int j = s;j <= t;j++)
		{
			if(i >= j) 
			dp[i] = min(dp[i],dp[i-j]+f[i]);
		}
	}
	for(int i = p;i <= p+10;i++)
	{
		mi = min(mi,dp[i]);
	}
	cout << mi;
    return 0;
}

时间复杂度为O(p*(t-s))。

猜你喜欢

转载自blog.csdn.net/zhi6fui/article/details/132069848
今日推荐