PAT A1044 Shopping in Mars 在火星购物【二分】

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.

在火星购物是一场十分不同的体验。火星人用链状钻石付钱。每一个钻石都有一定的价值(单位是M$)。当支付时,链条可以从任意位置切断一次,钻石一个接一个取下来。一旦钻石取出链条就不能再放回去。比如说,我们有一个带有8个钻石的链条,每一个钻石的价格是3,2,1,5,4,6,8,7.我们必须支付15M$,我们有3中方案

1.在4,6之间剪断链条,拿出位置1到位置5的钻石(3+2+1+5+4=15)

2.在5之前或者在6之后剪断链条,拿出位置4到位置6的钻石(5+4+6=15)

3.在8之前剪断链条,取位置7到8的钻石(8+7=15)

现在给出一串钻石的价值和顾客需要支付的钱数,你需要列出所有的支付方案

如果不能付出精确的价格,你必须找到最小花费的方案

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 numbers: N (≤10^​5​​), the total number of diamonds on the chain, and M (≤10^​8​​), 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.

每一个测试样例,第一行包含2个数:N(≤10^5)---链条上的钻石总数。M(≤10^8)---顾客需要支付的钱

下一行包含n个正数D1...DN(Di≤10^3)----代表钻石的价值

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.

对于每一个测试样例,打印出i-j,i≤j并且Di+....+Dj=M.如果有多个方案,所有的方案按照i递增的顺序输出。

如果没有方案,输出i-j,i≤j并且Di+....+Dj>M并且Di+....+Dj-M最小。如果有多个方案,按照i递增的顺序输出

#include<cstdio>
const int N=100010;
int sum[N];
int n,S,nearS=100000010;
//返回在(L,R)内第一个大于S的位置 
int upper_bound(int L,int R,int x){
	int left=L,right = R,mid;
	while(left<right){
		mid=(left+right)/2;
		if(sum[mid]>x){
			right=mid;
		}else{
			left=mid+1;
		}
	}
	return left;
}

int main(){
	scanf("%d%d",&n,&S);
	sum[0]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&sum[i]);
		sum[i]+=sum[i-1];
	}


	for(int i=1;i<=n;i++){//枚举左端点 
		int j=upper_bound(i,n+1,sum[i-1]+S);//求右端点 j是第一个和值大于S的位置
		if(sum[j-1]-sum[i-1]==S){//所以下标sum[j-1]-sum[i-1]小于或等于S
			nearS=S;//等于S即有解决方案,跳出循环
			break;
		}else if(j<=n&&sum[j]-sum[i-1]<nearS){//如果小于S,sum[j]-sum[i-1]就是当前循环第一                
			nearS=sum[j]-sum[i-1];            //个大于S的,如果比之前的nearS小,就更新
		}
	}


	for(int i=1;i<=n;i++){
		int j=upper_bound(i,n+1,sum[i-1]+nearS);//求右端点,如果有解决方案,nearS=S,没有则是
		if(sum[j-1]-sum[i-1]==nearS){            //最接近S的
			printf("%d-%d\n",i,j-1);
		} 
	}
	return 0;
}

思路:令Sum[i]表示A[1]到A[i]的和值,即令Sum[i]=a[1]+a[2]+....+a[i]。因为序列都是正值,因此Sum[i]一定是严格单调递增的,即有Sum[1]<Sum[2]....<Sum[n]成立。如果要求连续子序列A[i]到A[j]的和值,只需要计算Sum[j]-Sum[i-1]即可。

既然Sum数组严格单调递增,那就可以用二分法来做这道题。假设需要在序列A[1]~A[n]中寻找和值为S的连续子序列,就可以枚举左端点i,然后再Sum数组的[i,n]范围内查找值为Sum[i-1]+S的元素(由Sum[j]-Sum[i-1]=S推得)是否存在:如果存在,则把对应的下标作为右端点j;如果不存在,找到第一个使和值超过S的右端点j。

考虑到题目要求输出所有方案,因此需要对序列进行两次遍历,其中第一次遍历求出大于等于S的最接近S的和值nearS;第二次遍历找到那些和值恰好为nearS的方案并输出

猜你喜欢

转载自blog.csdn.net/qq_38179583/article/details/86647991
今日推荐