【PAT-A-1044】Shopping in Mars (25) 时间复杂度为O(n)的简单方法

题目大意:给一个价格M,一段正整数序列,要求找出所有的字串,其数字之和刚好是M。如果找不到这样的字串,那就找出比M大且最接近的一个数字,并找出所有字串。

分析:乍一看有点麻烦,似乎要找两遍,一次看看有没有等于M的,没有的话再找出一个比M大的。其实完全可以认为是找出一个大于等于M并尽可能小的数,最小的情况就是等于M了,这样就归结为一个问题了。

本题常规做法是二分查找,算出从第一点开始,以第i个下标作为终点的字串和数组sum[i],这样就得到了递增序列,可以用sum[j]-sum[i-1]来代表从i到j的字串和。该方法的代码网上很多,时间复杂度是O(nlogn),这里给出一种更为简单,代码更短,时间复杂度只有O(n)的方法。

#include
#include
using namespace std;

struct node{
	int left,right;
	node(int l, int r):left(l),right(r){}
}; //结构体,用来存结果的头尾

int num[100005];//存放初始序列
vector res;//存放结果
int MIN = 1e9;//存放最小值

int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i = 1; i <= n; ++i){
		scanf("%d",&num[i]);
	}
	int left = 1, right = 1;//left为起始,right为终点,都为闭区间
	int s = num[left];//s代表sum,总和的意思,一开始等于sum[1,1]
	while(right <= n){
		if(s >= m){
			if(s < MIN){ //找到了更小的,清空并更新
				MIN = s;
				res.clear();
				res.push_back(node(left,right));
			}else if(s == MIN) //找到了一样大的,记录字串起点和终点
				res.push_back(node(left,right));
			s -= num[left];//总和大了,那就通过右移起点的方法来减小sum
			left++;
		}else{
			right++;//总和不够大,那就通过右移终点的方法来增大sum
			s += num[right];
		}
	}
	for(int i = 0; i < res.size(); ++i){//输出结果
		printf("%d-%d\n",res[i].left,res[i].right);
	}
	return 0;
} 

【PAT-A-1044】Shopping in Mars (25) 时间复杂度为O(n)的简单方法_第1张图片

你可能感兴趣的:(C++算法与竞赛)