【1044 25 二分 双指针】 Shopping in Mars

传送门

题意

给定 \(n,m\) ,长度为 \(n\) 的序列,求出所有和为 \(m\) 的子序列的左右端点下标,如果没有就找到大于等于的最小的

数据范围

\(n\leq 10^{5}\)
\(m\leq 10^{8}\)

题解

  • 双指针
    • 满足条件的后移动左指针,累积的和减去其值
    • 不满足条件移动右值针,累积和加上值
  • 二分做法
    • 累计前缀和,前缀和是单调的,
    • 枚举左端点,二分右端点即可

Code

二分
#include <bits/stdc++.h>
using namespace std;

vector<int> prefix;

int main() {
	int n, m; cin >> n >> m;
	prefix.resize(n + 1);
	
	for (int i = 1; i <= n; i++) {
		int x; cin >> x;
		prefix[i] = prefix[i - 1] + x;
	}
	vector<pair<int, int>> ans;
	int mi = INT_MAX;
	for (int i = 1; i <= n; i++) {
		int l = i, r = n;
		while (l < r) {
			int mid = (l + r) / 2;
			if (prefix[mid] - prefix[i - 1] >= m) {
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		int res = prefix[l] - prefix[i - 1];
		if (res > mi) continue;
		if (res >= m) {
			if (res < mi) {
				mi = res;
				ans.clear();
			}
			ans.push_back({i, l});
		}
	}	
	for (auto& it : ans) cout << it.first << '-' << it.second << endl;

}
双指针
#include <bits/stdc++.h>
using namespace std;

int main() {
	int n, m; cin >> n >> m;
	vector<int> seq(n);
	for (auto& it : seq) cin >> it;
	int sum = 0, mi = INT_MAX;
	vector<pair<int, int>> ans;
	for (int i = 0, j = 0; i < n and j <= n;) {
		if (sum >= m) {
			if (sum < mi) {
				mi = sum;
				ans.clear();
				ans.push_back({i + 1, j});
			} else if(sum == mi) {
				ans.push_back({i + 1, j});
			}
			sum -= seq[i++];
		} else {
			sum += seq[j++];
		}
	}
	for (int i = 0; i < ans.size(); i++) {
		cout << ans[i].first << '-' << ans[i].second;
		if (i < ans.size() - 1) cout << endl;
	}
}
posted @ 2021-02-22 01:32  Hyx'  阅读(36)  评论(0)    收藏  举报