【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;
}
}

浙公网安备 33010602011771号