PAT 1044. Shopping in Mars

写起来还挺麻烦的。因为一看数列可能会长到10^5,所以求解的时候,不能硬着来,比方说寻找一个数段i~j,使得Di加到Dj之和尽量接近题目给定的值M,那么在i既定的情况下,要找这个j,就不能一个个遍历过来,否则求整个题目的解就需要n^2复杂度。而且为了保险,计算Di加到Dj的和也不要一个个加过来,也要用log(n)的复杂度来,这部分我是用树状数组实现的。

#include<cstdio>
#include<cstring> 
#include<iostream>
using namespace std;
const int N=100005;
long long tree[N];
long long inpu[N];
int n;
long long target;
long long calcu(int x){//求和函数,tree[]数组就是树状数组
	if(x>n)return -1;
	long long re=0;
	while(x>0){
		re+=tree[x];
		x-=(x&-x);
	}
	return re;
}

typedef struct message{//用二分法求解的时候,既需要知道数段的起点、终点,还需要知道数段和与题目给定的值之差,所以我用结构体保存解的信息
	int from,to;
	long long more;
	void oupu(){
		//printf("%d-%d more=%lld\n",from,to,more);
		printf("%d-%d\n",from,to);
	}
}message;
message ans[N];
int cnt;
message binary_find(int x){
	message re;
	re.from=x;
	long long now=calcu(x-1),then;
	if(calcu(n)-now<target){//如果从给定的Dx开始一直加到Dn也不能达到target,那说明以Dx为开始的数段没有一个能成为合法解
		re.more=999999999;//所以把差值设成九个九,保证它返回回去了以后也不会被留在ans[]数组里
		return re;
	}
	if(calcu(x)-now>target){//如果光是Dx就已经大于target了,那也不用再二分查找了,Dx就是数段的终点
		re.to=x;
		re.more=calcu(x)-now;
		return re;
	}
	int lf=x,rt=n,mid=(lf+rt)/2;
	while(1){<span style="white-space:pre">	</span>//二分查找
		then=calcu(mid)-now;
		if(then==target){
			re.to=mid;
			re.more=0;
			return re;
		}
		if(lf==rt){
			if(then>target){
				re.to=mid;
				re.more=then-target;
				return re;
			}
			else if(then<target){
				re.to=mid+1;
				re.more=calcu(mid+1)-now-target;
				return re;
			}
		}
		
		if(then>target){
			rt=mid;
			mid=(lf+rt)/2;
		}
		else if(then<target){
			lf=mid+1;
			mid=(lf+rt)/2;
		}
	}
}
int main(){
	int i;
	scanf("%d%lld",&n,&target);
	memset(tree,0,sizeof(tree));
	int temp;
	for(i=1;i<=n;i++){<span style="white-space:pre">		</span>//读入数列,并且把树状数组处理好,以供calcu()函数使用
		scanf("%lld",&inpu[i]);
		temp=i;
		while(temp<=n){
			tree[temp]+=inpu[i];
			temp+=(temp&-temp);		
		}
	}
	message msg;
	for(i=1;i<=n;i++){
		msg=binary_find(i);
		//msg.oupu();
		if(cnt==0||msg.more<ans[0].more){//如果 当前解 比 原有解 的差值更小,那就把原有解都舍弃掉,只保存当前解
			ans[0]=msg;
			cnt=1;
		}
		else if(msg.more==ans[0].more){  //如果 当前解 与 原有解 的差值一样大,那就把它也存进去
			ans[cnt++]=msg;
		}
	}
	for(i=0;i<cnt;i++)
		ans[i].oupu();
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值