PAT Advanced1044 Shopping in Mars(二分法)

本文介绍了PAT Advanced1044题目的解决方案,该题目涉及在火星上使用钻石链支付的场景。当需支付的金额给定时,需要找出所有连续钻石链的组合,如果无法精确匹配,应找到最接近的组合。原始算法时间复杂度为O(N^2),通过引入二分法优化为O(NlogN),利用预先计算的数组存储部分和,以提高效率。
摘要由CSDN通过智能技术生成

链接:PAT Advanced1044

Shopping in Mars is quite a different experience. The Mars people pay by chained diamonds. Each diamond has a value (in Mars dollars M$). When making the payment, the chain can be cut at any position for only once and some of the diamonds are taken off the chain one by one. Once a diamond is off the chain, it cannot be taken back. For example, if we have a chain of 8 diamonds with values M$3, 2, 1, 5, 4, 6, 8, 7, and we must pay M$15. We may have 3 options:

  1. Cut the chain between 4 and 6, and take off the diamonds from the position 1 to 5 (with values 3+2+1+5+4=15).
  2. Cut before 5 or after 6, and take off the diamonds from the position 4 to 6 (with values 5+4+6=15).
  3. Cut before 8, and take off the diamonds from the position 7 to 8 (with values 8+7=15).

    Now given the chain of diamond values and the amount that a customer has to pay, you are supposed to list all the paying options for the customer.
    If it is impossible to pay the exact amount, you must suggest solutions with minimum lost.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 numbers: N (≤105), the total number of diamonds on the chain, and M (≤108​), the amount that the customer has to pay. Then the next line contains N positive numbers D​1​​ ⋯ D​N​​ (D​i​​ ≤10​3​ for all i=1,⋯,N) which are the values of the diamonds. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print i-j in a line for each pair of i ≤ j such that Di + … + Dj = M. Note that if there are more than one solution, all the solutions must be printed in increasing order of i.

If there is no solution, output i-j for pairs of i ≤ j such that Di + … + Dj >M with (Di + … + Dj −M) minimized. Again all the solutions must be printed in increasing order of i.

It is guaranteed that the total value of diamonds is sufficient to pay the given amount.

Sample Input 1:

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

Sample Output 1:

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

Sample Input 2:

5 13
2 4 5 7 9

Sample Output 2:

2-4
4-5


题目大意: 火星人用钻石链购物,输入链上钻石数N和需支付的金额M,随后给出N个钻石的价值。找出链上所有和等于M的连续钻石链,若没有,则找出所有大于且最接近M的连续钻石链,输出按链中第一个钻石序号升序输出。



一开始的想法就是以 i 为起点( i = 0,1,2,…,N),每次 ji 遍历到N,找出并存储每次第一个和≥M的链,然后输出最小的链。如下:

#include<bits/stdc++.h>
using namespace std;
struct part
{ 
	int i,j;
	int value;
};
bool cmp(part a,part b)
{
	if(a.value!=b.value)
		return a.value<b.value;
	else
		return a.i<b.i;
}
int main()
{  
	
	int a[100010],N,M;
	vector<part> ans;
	int i,j,sum;
	cin>>N>>M;
	for(i=1;i<=N;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=N;i++)
	{
		sum=0;
		for(j=i;j<=N;j++)
		{
			sum+=a[j];
			if(sum>=M)    //第一个 ≥M的链
			{
				part t={i,j,sum};
				ans.push_back(t);
			}
		}
	}
	sort(ans.begin(),ans.end(),cmp); 
	for(i=0;ans[i].value==ans[0].value;i++)  //输出符合 ≥M的最小链
		printf("%d-%d\n",ans[i].i,ans[i].j);
	return 0;
}

这个在测试点2,3,5会超时,有两层循环,很明显该算法的时间复杂度为O( N2 )
那么就要进行相应的优化。


可以看到在第二层循环中, i 不变,那么每次求出来的和是递增序列,那么就可以使用二分法找到第一个令和 ≥M的 j 。将时间复杂度优化为O(NlogN)

那么又有一个问题,因为二分法不是顺序遍历,每次都要对区间重新求和,若挨个相加又会增加时间。这里又要用到一个方法:用一个s[maxn] 数组来存储 1 ~ i 的和,那么i ~ j 的和就等于s[j]-s[i-1]。

最终代码如下:

#include<bits/stdc++.h>
using namespace std;
struct part
{ 
	int i,j;
	int value;
};
int s[100010],a[100010],N,M;;
int find_j(int L,int R,int i)  //二分法找到第一个令和 ≥M的 j
{
	while(L<R)   //L==R时即退出,得到目标
	{
		int mid=(L+R)/2;
		if(s[mid]-s[i-1]>=M)
			R=mid;
		else
			L=mid+1;
	}
	if(s[L]-s[i-1]>=M)  //注意,有可能区间内没有符合条件的
		return L;       //此时会有L==R==N,但并非正确值,所以还要进行判断。
	else
		return 0;       //不符合返回0
}
bool cmp(part a,part b)
{
	if(a.value!=b.value)
		return a.value<b.value;
	else
		return a.i<b.i;
}
int main()
{   
	vector<part> ans;
	scanf("%d %d",&N,&M);
	int i,j;
	s[0]=0;
	for(i=1;i<=N;i++)
	{
		scanf("%d",&a[i]);
		s[i]=s[i-1]+a[i];
	}
	for(i=1;i<=N;i++)
	{
		if(j=find_j(i,N,i))
		{
			part t={i,j,s[j]-s[i-1]};
			ans.push_back(t);
		}
	}
	sort(ans.begin(),ans.end(),cmp); 
	for(i=0;ans[i].value==ans[0].value;i++)
		printf("%d-%d\n",ans[i].i,ans[i].j);
	return 0;
}

总结: 利用二分法有序序列中,可以寻找第一个满足某种条件的元素的位置。时间复杂度为O(logN)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值