2023蓝桥杯省模拟赛附近最小
这个题算是一个经典的数据结构入门题了,写了几个解法水一篇文章
map维护
时间复杂度nlgn,但是常数比较大,所以只能过90%数据
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int a[N];
void solve()
{
int n, k;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
cin >> k;
map<int, int>mp;
int l = 1, r = min(k + 1, n);
for (int i = 1; i <= r; i++)
mp[a[i]]++;
cout << mp.begin()->first;
for (int i = 2; i <= n; i++) {
int l1 = max(1, i - k), r1 = min(n, k + i);
if (l1 > l)
mp[a[l]]--;
if (r1 > r)
mp[a[r1]]++;
if (mp[a[l]] == 0)
mp.erase(a[l]);
l = l1, r = r1;
cout << " " << mp.begin()->first;
}
cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
单调队列
这算是题解区看到的最多的解法了,刚开始也没往这方面想,直接无脑贴了个线段树上去
代码里面有注释
时间复杂度是线性的,但是deque的常数是stl里面都算很大的,所以也比较慢,可以尝试用数组模拟deque
#include<iostream>
#include<deque>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
int a[N];
int main()
{
int n, k;
cin >> n;
deque<int> dq;
for (int i = 1; i <= n; i++)
cin >> a[i];
cin >> k;
for (int i = n + 1; i <= n + k; i++)
a[i] = 2 * N;
for (int i = 1; i <= n + k; i++)
{
while (!dq.empty() && a[dq.back()] > a[i])
dq.pop_back();
dq.push_back(i);
while (i - 2 * k > dq.front())
dq.pop_front();
if (i > k) cout << a[dq.front()] << " ";
}
return 0;
}
//为了方便判断范围所以将数组的下标索引加入队列。
// //使用双端队列实现滑动窗口,窗口每次向后移动,若新的数字比队尾大,
//直接进入队列, 比队尾小,需要弹出队列中所有比新的数字小的值,
//保证队头保存的是当前窗口中最小的值
//通过维护一个单调递增的队列,可以得到一个滑动窗口的最小值,
//并将他加入队列。例如窗口[7, 4, 3] 的最小值就是 3
//,那么对于题目要求的一定范围我们只需要每次输出前判断队首是否超过范围就行了,
//如果超过范围就将队首弹出,那么队列就只剩下[3] 了。
树状数组
把窗口中的数投到长度为1e6的数轴上,每投影一个数在数轴对应位置+1,然后转换成了求第一个数轴的前缀和大于等于1的点的位置,二分即可,当然这个前缀和也可以用线段树维护
这个解法的局限性在于a[i]的值域,如果值域过大,比如1e18,那肯定开不下这么大的数组,就需要离散化一下了
下面提供非离散化和离散化的代码
非离散化
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int tree[N], a[N];
int lowbit(int x) {
return x & (-x);
}
int sum(int x) {
int ans = 0;
while (x) {
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
void change(int x, int p) {
while (p<N) {
tree[p] += x;
p += lowbit(p);
}
}
void solve()
{
int n,k;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
int mi = 0x3f3f3f3f;
cin >> k;
int l = 1, r = min(k + 1, n),x=0x3f3f3f3f;
for (int i = 1; i <= r; i++)
change(1, a[i]),mi=min(a[i],mi);
cout << mi;
for (int i = 2; i <= n; i++) {
int l1 = max(1, i - k), r1 = min(n, k + i);
if (l1 > l)
change(-1, a[l]);
if (r1 > r)
change(1, a[r1]);
int x = 1, y = 1e6 + 1;
while (x < y) {
int mid = x + y >> 1;
if (sum(mid) >= 1)
y = mid;
else
x = mid + 1;
}
l = l1, r = r1;
cout << " " << y;
}
cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
离散化
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int tree[N], a[N],b[N];
int len = 0, n, k;
int lowbit(int x) {
return x & (-x);
}
int sum(int x) {
int ans = 0;
while (x) {
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
void change(int x, int p) {
while (p<=n) {
tree[p] += x;
p += lowbit(p);
}
}
int serach(int x) {
int l = 1, r = len + 1;
while (l < r) {
int mid = l + r >> 1;
if (b[mid] == x)
return mid;
else if (b[mid] < x)
l = mid + 1;
else
r = mid;
}
return r;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i],b[i]=a[i];
int mi = 0x3f3f3f3f;
cin >> k;
sort(b + 1, b + n + 1);
for ( int j = 1; j <= n; j++) {
if (b[j] != b[j-1])
b[++len] = b[j];
}
int l = 1, r = min(k + 1, n),x=0x3f3f3f3f;
for (int i = 1; i <= r; i++)
change(1, serach(a[i])),mi=min(a[i],mi);
cout << mi;
for (int i = 2; i <= n; i++) {
int l1 = max(1, i - k), r1 = min(n, k + i);
if (l1 > l) {
change(-1, serach(a[l]));
}
if (r1 > r)
change(1, serach(a[r1]));
int x = 1, y = len + 1;
while (x < y) {
int mid = x + y >> 1;
if (sum(mid) >= 1)
y = mid;
else
x = mid + 1;
}
l = l1, r = r1;
cout << " " << b[y];
}
cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
类似题
力扣342周赛也考了个类似的题,也可以用树状数组求解
class Solution {
public:
int tree[110];
int lowbit(int x){
return x&-x;
}
int sum(int x){
int ans=0;
while(x){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
void change(int pos,int x){
while(pos<=105){
tree[pos]+=x;
pos+=lowbit(pos);
}
}
vector<int> getSubarrayBeauty(vector<int>& a, int k, int x) {
vector<int>ans;
for(int i=0;i<k;i++)
change(a[i]+51,1);
int l=1,r=105;
while(l<r){
int mid=l+r>>1;
if(sum(mid)>=x)
r=mid;
else
l=mid+1;
}
if(r-51<0)
ans.push_back(r-51);
else
ans.push_back(0);
for(int i=1;i+k-1<a.size();i++){
change(a[i-1]+51,-1);
change(a[i+k-1]+51,1);
l=1,r=105;
while(l<r){
int mid=l+r>>1;
if(sum(mid)>=x)
r=mid;
else
l=mid+1;
}
if(r-51<0)
ans.push_back(r-51);
else
ans.push_back(0);
}
return ans;
}
};
ST表
st表最经典的应用就是求解静态rmq问题了
下面用倍增跳表求解
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int st[N][21],lg2[N];
void solve()
{
int n,k;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> st[i][0];
for (int i = 2; i <= n; i++)
lg2[i] = lg2[i / 2] + 1;
cin >> k;
for (int i = 1; (1 << i) <= n; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++)
st[j][i] = min(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
}
for (int i = 1; i <= n; i++) {
int l = max(1, i - k), r = min(n, k + i);
int len = r - l + 1,mi=0x3f3f3f3f;
//cout << i << '\n';
while (len) {
mi = min(mi, st[l][lg2[len]]);
l += (1 << lg2[len]);
len -= (1 << lg2[len]);
}
cout << mi;
if (i < n)
cout << ' ';
else
cout << '\n';
}
//cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
线段树
线段树无脑维护就行了,只需要写查询
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int a[N];
struct tnode {
int l, r;
int sum;
};
struct Segment_Tree
{
tnode t[4 * N];
void build(int root, int l, int r, int* A)
{
t[root].l = l, t[root].r = r;
if (l == r)
t[root].sum = A[l];
else {
int ch = root << 1, mid = l + r >> 1;
build(ch, l, mid, A);
build(ch + 1, mid + 1, r, A);
t[root].sum = min(t[ch].sum , t[ch + 1].sum);
}
}
int query_min(int root, int l, int r)
{
if (l == t[root].l && r == t[root].r)
return t[root].sum;
int mid = t[root].l + t[root].r >> 1;
if (r <= mid)
return query_min(root << 1, l, r);
else if (l > mid)
return query_min(root << 1 | 1, l, r);
else
return min(query_min(root << 1, l, mid) , query_min(root << 1 | 1, mid + 1, r));
}
};
Segment_Tree st;
void solve()
{
int n,k;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
cin >> k;
st.build(1, 1, n, a);
for (int i = 1; i <= n; i++) {
int l = max(1, i - k), r = min(n, k + i);
if (i < n)
cout << st.query_min(1, l, r) << ' ';
else
cout << st.query_min(1, l, r) << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
分块
这题分块有可能可以过但也有可能过不了,取决于块大小的取值,试了不少的块取值还是没过最后一组样例
下面的代码块取得比较小,但是如果块大小取值改一下,数据再强一些,就不只是运行超时了,还会出现答案错误,可以自己去想一想然后试一下,这份代码运气好,所以只运行超时了
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<cmath>
using namespace std;
const int N = 1e6 + 10;
int a[N], b[N], mi[N], bg[N], ed[N], pos[N];
void slove() {
int n, k;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
cin >> k;
int block = sqrt(k), t;
t = n / block;
if (n % block)
t++;
for (int i = 1; i <= t; i++)
bg[i] = block * (i - 1) + 1, ed[i] = block * i;
ed[t] = n;
for (int i = 1; i <= n; i++)
pos[i] = (i - 1) / block + 1;
for (int i = 1; i <= t; i++) {
mi[i] = 0x3f3f3f3f;
for (int j = bg[i]; j <= ed[i]; j++)
mi[i] = min(a[j], mi[i]);
}
for (int i = 1; i <= n; i++) {
int l = max(1, i - k), r = min(n, k + i), x, y;
if (l > bg[pos[l]])
x = pos[l] + 1;
else
x = pos[l];
if (r < ed[pos[r]])
y = pos[r] - 1;
else
y = pos[r];
int ans = 0x3f3f3f3f;
for (int i = l; i < bg[x]; i++)
ans = min(a[i], ans);
for (int i = x; i <= y; i++)
ans = min(mi[i], ans);
for (int i = bg[y] + 1; i <= r; i++)
ans = min(a[i], ans);
cout << ans;
if (i < n)
cout << ' ';
else
cout << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
T = 1;
//cin >> T;
while (T--) {
slove();
}
return 0;
}