A1044 Shopping in Mars

题意:给出一个数字序列与一个数M,在数字序列中求出所有和值为s的连续子序列(区间下标左端点小的先输出)。若没有这样的序列,求出和值恰好大于S的子序列(即在所有和值大于M的子序列中和值最接近S)。假设序列下标从1开始。

 

思路:令sum[i]表示A[1】到A[i]的和值(A表示输入的数组)。Sum[i]严格遵守递增序列,因此只需要计算sum[j]-sum[i-1],即可得到和值为M的连续子序列。

递增序列,利用二分法:在sum数组查找第一个大于sum[i-1]+M的的元素的值,令其为sum[j]。   利用upper_bound。二分法查找

 //求序列中第一个大于X的元素的位置
	 int upper_bound(int a[],int left,int right,int x){
	 	int mid;
	 	while(left<right){
	 		mid=(left+right)/2;
	 		if(a[mid]>x)right=mid;
	 		else left=mid+1;
		 }
		 return left;
	 } 

那么在J前面一个位置有可能满足sum[j-1]+sum[i-1]==M,因此通过的J值可以分两个步骤,先查看能否找到=M,如果不能则求出最接近M的值。  

考虑题目要输出所有方案,因此要遍历两次序列,第一次查找出M的值或者是最接近M的数字,第二次输出方案。 

#include<cstdio>
int sum[110];
//返回第一个大于X的整数的位置 
int search(int left,int right,long x){   
	int mid;
	while(left<right){ 
		mid=(left+right)/2;
        if(sum[mid]>x)right=mid;        //此处如果sum[maxn]定义在Main 方法中,则会报错 
		else left=mid+1;
	}
	return left;
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	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++){
		printf("%d",sum[i]);
		if(i<n)printf("-");
	}	
printf("\n");
int nearS=1000010;   //先定义nearS为一个较大的数字,之后不断更新nearS 
for(int i=1;i<=n;i++){
	int j=search(i,n+1,sum[i-1]+m);   //查找在[i,n]范围, 注意是sum[i-1]+m 
	if(sum[j-1]-sum[i-1]==m){          //这里变成了j-1,因为查找的j是第一个大于sum[i-1]+m的数 
		nearS=m;
		break;
	}else if(j<=n&&sum[j]-sum[i-1]<nearS) {
		nearS=sum[j]-sum[i-1];   //不断更新最小nearS的值 
			}
}
for(int i=1;i<=n;i++){
	int j=search(i,n+1,sum[i-1]+nearS);
	if(sum[j-1]-sum[i-1]==nearS){
		printf("%d-%d\n",i,j-1);
	}
} 

	return 0;
	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值