PAT-ADVANCED1044——Shopping in Mars

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805439202443264

题目描述:

题目翻译:

1044 火星购物

在火星购物是一种完全不同的体验。火星人用链式钻石付钱。每颗钻石都有一个值(火星美元M $)。在付款时,链条可以在任何位置切割一次,一些钻石一个接一个地从链条上取下。一旦钻石离开链条,它就不能被收回。例如,如果我们有一个8颗钻石链,价值M $ 3,2,1,5,4,6,8,7,我们必须支付M $ 15。 我们可能有3种选择:

切断4到6之间的链条,从1到5位置取下钻石(价值3 + 2 + 1 + 5 + 4 = 15)。
在5或6之后切割,并从4到6位置取下钻石(价值5 + 4 + 6 = 15)。
在8之前切割,并从7到8位置取下钻石(价值8 + 7 = 15)。
现在,鉴于钻石价值链和客户必须支付的金额,你需要列出客户的所有付款选项。

如果无法支付确切的金额,你必须建议最小损失的解决方案。

输入格式:

每个输入文件包含一个测试用例。对每个测试用例,第一行包含2个数字:N(<= 10 ^ 5),代表链上的钻石总数,以及M(<= 10 ^ 8),代表顾客需要支付的总额。接下来的一行给出N个正数:D1, ..., DN(Di <= 10 ^ 3, i = 1, 2, ..., N),代表每颗钻石的价值。一行中的所有数字由一个空格分隔。

输出格式:

对每个测试用例,如果Di + ... + Dj == M,则在一行中打印i-j。如果有多个不同的答案,按i的増序打印结果。如果找不到答案,找出Di + ... + Dj > M且Di + ... + Dj - M最小的解决方案,再一行中打印i-j。同样所有的答案应按i的増序打印结果。题目保证钻石总额足够支付给出的需支付总金额。

输入样例1:

16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13

输出样例1:

1-5
4-6
7-8
11-11

输入样例2:

5 13
2 4 5 7 9

输出样例2:

2-4
4-5

知识点:二分查找法

思路:先计算一个元素代表1, ..., i钻石总额的数组sum

sum[0]设为0。sum[i]设为1, ..., i钻石总额。用一个vector<pair<int, int> > result保存结果。

对每个位置i(i = 1, ..., N),用lower_bound()函数寻找大于等于i的另一个索引j,使得sum[j] - sum[i - 1] >= M(这就是为什么要设sum[0]为0的原因)。

注意lower_bound()函数返回的是一个指针,将其与数组首地址sum相减才得索引值

(1)如果能找到sum[j] - sum[i - 1] == M,将i和j加入结果result之前需要判断result是否为空。

a:如果result为空,直接把i和j加入即可。

b:如果result不为空,

b-1:如果result中的最后的一个元素不满足索引间的和为M,需要先清空result,再把i和j加入。

b-2:如果result中的最后的一个元素满足索引间的和为M,直接把i和j加入即可。

(2)如果不能找到sum[j] - sum[i - 1] == M,将i和j加入结果result之前也要判断result是否为空。

a:如果result为空,直接把i和j加入即可。

b:如果result不为空,

b-1:如果result中的最后的一个元素索引间的和比sum[j] - sum[i - 1]要大,需要先清空result,再把i和j加入。

b-2:如果result中的最后的一个元素索引间的和与sum[j] - sum[i - 1]相等,直接把i和j加入即可。

b-3:如果result中的最后的一个元素索引间的和比sum[j] - sum[i - 1]要小,当前i和j不加入。

时间复杂度是O(NlogN)。空间复杂度是O(N)。

C++代码:

#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>

using namespace std;

int main() {
	int N, M;
	scanf("%d %d", &N, &M);
	int nums[N + 1];	//必须在N的值输入之后再来定义该数组
	nums[0] = 0;
	for(int i = 1; i <= N; i++) {
		scanf("%d", &nums[i]);
	}
	int sum[N + 1];	//sum[i]存储nums[1] + ... + nums[i]的值,i >= 1
	for(int i = 0; i <= N; i++) {
		if(i == 0) {
			sum[i] = nums[i];
		} else {
			sum[i] = nums[i] + sum[i - 1];
		}
	}
	vector<pair<int, int> > result;
	for(int i = 1; i <= N; i++) {
		int find = sum[i - 1] + M;
		int j = lower_bound(sum + i, sum + N + 1, find) - sum;
		if(sum[j] == find) {
			if(result.empty()) {	//如果result为空,直接存入 
				result.push_back(make_pair(i, j));
			} else {
				pair<int, int> p = result[result.size() - 1]; 
				if(sum[p.second] - sum[p.first - 1] - M != 0) {	//判断result中的最后一个元素的索引i和j之间的值的和是否刚好为M 
					result.clear();
					result.push_back(make_pair(i, j));
				} else {
					result.push_back(make_pair(i, j));
				}
			}
		} else if(sum[j] > find) {
			if(result.empty()) {
				result.push_back(make_pair(i, j));
			} else {
				pair<int, int> p = result[result.size() - 1];
				if(sum[p.second] - sum[p.first - 1] - M > sum[j] - sum[i - 1] - M) {
					result.clear();
					result.push_back(make_pair(i, j));
				} else if(sum[p.second] - sum[p.first - 1] - M == sum[j] - sum[i - 1] - M) {
					result.push_back(make_pair(i, j));
				}
			}
		}
	}
	for(int i = 0; i < result.size(); i++) {
		printf("%d-%d\n", result[i].first, result[i].second);
	}
}

C++解题报告:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值