PTA A1044Shopping in Mars (25分)

題目:click me~

題意:給出一個數字序列和一個數S,在數字序列中求出所有和值爲S的連續子序列,(區間下標左端點小的先輸出,左端點相同時右端點小的先輸出)。若沒有這樣的序列,求出和值大於S但最小的子序列。

解題思路:

令sum[i]表示A[1]到A[i]的和值,由於序列都是正值,因此sum[i]一定是嚴格單調遞增的,(初始化sum[0]=0)。這樣做的好處在於,若要求連續子序列A[i]到A[j]的和值,只需要計算sum[j]-sum[i-1]即可。

有了嚴格單調遞增的sum數組,那就可以用二分法來做這道題。枚舉左端點i(1<=i<=n),然後在sum數組(i,n)的範圍內查找值爲sum[i-1]+S的元素是否存在:如果存在,用對應的下標j做右端點;如果不存在,找到第一個使和值超過S的右端點j.(可以用upper_bound函數來寫)

考慮到題目要求輸出所有方案,因此需要對數組進行兩次遍歷,第一次遍歷求出大於等於S的值nearS,第二次遍歷求出和值爲S的方案並輸出。總複雜度爲O(nlogn)。

code

#include<iostream>
using namespace std;
const int N = 100010;
int sum[N];
int n, S, nearS = 100000010;
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() {
	cin >> n >> S;
	sum[0] = 0;
	for (int i = 1;i <= n;i++) {
		int a;
		cin >> a;
		sum[i] = sum[i - 1] + a;
	}
	for (int i = 1;i <= n;i++) {
		int j = upper_bound(i, n + 1, sum[i - 1] + S);
		if (sum[j - 1] - sum[i - 1] == S) {
			nearS = S;
			break;
		}
		else if (j <= n && sum[j] - sum[i - 1] < nearS)nearS = sum[j] - sum[i - 1];
	}
	for (int i = 1;i <= n;i++) {
		int j = upper_bound(i, n + 1, sum[i - 1] + nearS);
		if (sum[j - 1] - sum[i - 1] == nearS)
			cout << i <<"-"<< j - 1<<endl;
	}
	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章