算法之单调队列

前序:来自b站左程云,个人进行笔记整理,如有错误,感谢指出!

  1. 窗口中的新成员如果比队列的队头成员大,队列直接清空,加入新成员(因为新成员又新又大)
  2. 窗口中的新成员如果比队列的队尾大,则从队尾开始,所有比新成员小的队尾元素全部删了(因为新成员比队尾的新且大)
  3. 窗口中的新成员如果比队列的队尾小,那么加入到队尾,因为他比较新,有可能是后续的最大值

题目一:个人开始认为的疑难点:按照前面分析说的,保持队头为每个窗口的最大值,当窗口更新的时候怎么知道是下一个窗口的最大值,后面发现l,r指针控制,而队列仅仅维护每次的最大值即可。最大值,优先队列结构也可以处理。

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

const int N = 1e6 + 10;
int help[N],arr[N];
int h,t;

int main()
{
     int n , k;
	 cin >> n >> k;
	 for(int i = 0 ; i < n ; i++)
	 cin >> arr[i];
	 
	 h = t = 0;
	 //先形成k - 1 的窗口,大->小 
	 for(int i = 0 ; i < k - 1 ; i++)
	 {
	 	while(h < t && arr[help[t - 1]] <= arr[i])//h <t 代表队列不为空,结构被破坏
		{
		   t--;	
		}
	    help[t++] = i; 
	 }
	 
	 int m = n - k + 1;//这里通过枚举发现的数与数的关系 ,得到解的个数与n k的关系 
	 int ans[m + 1];
	 //当前窗口k - 1长度? 

	 for(int l = 0 , r = k - 1; l < m ; l ++ , r++)//前面已经形成了k大小的窗口,所以下一个数的开始就是k - 1 ,开始继续判断 
	 {
	 	
	        while(h < t && arr[help[t - 1]] <= arr[r]){//枚举队列的数和进入的数相比,如果大于队列中的数直接弹出 
	    		t--;
			}
			//不大于进入 
			help[t++] = r;
			//结算答案 
			ans[l] = arr[help[h]];//546
			//看此时队列是否需要向左滑动。假设543,此时5是最大值。下标0,窗口滑出5已经和接下来的窗口没关系了,所以++, 
			if(help[h] == l){//为什么不等于就不加加呢? 563此时h位置是下标一不等于0,是因为此时的h位置依旧有可能为下一个位置的最大值 
				h++;//总结就是判断此时的值能不能依旧成为下一个最大值。 
			}
	 } 
	 
	 for(int i = 0 ; i < m ; i++)
	 cout << ans[i] << " ";
	 return 0;
	 	
}

题目二:

思路:既然是任意两个都不大于某个数x,那么窗口内最大和最小的两个数的绝对差也小于x,一旦下一个数进入窗口,就有可能有三种情况最大的数变了,破坏原平衡,最小的数变了,破坏原平衡,或者不破坏,依旧成立。

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

const int N = 1e6 + 10;
int arr[N], max1[N], min1[N];
int hmax, tmax, hmin, tmin;
int limit, n;

void Push(int r) {
    while (hmax < tmax && arr[max1[tmax - 1]] <= arr[r]) {
        tmax--;
    }
    max1[tmax++] = r;

    while (hmin < tmin && arr[min1[tmin - 1]] >= arr[r]) {
        tmin--;
    }
    min1[tmin++] = r;
}

void Pop(int l) {
    //与上面同样的逻辑有没有可能成为下一个窗口的最大值
    if (hmax < tmax && max1[hmax] == l) {
        hmax++;
    }

    if (hmin < tmin && min1[hmin] == l) {
        hmin++;
    }
}

bool judge(int number) {
    // 新进来的数 如果能入队列,与队列内的最大(小)值进行相比
    int cnt1 = hmax < tmax? max(arr[max1[hmax]], number) : number;
    int cnt2 = hmin < tmin? min(arr[min1[hmin]], number) : number;

    return cnt1 - cnt2 <= limit;
}


int solve() 
{
    hmax = tmax = hmin = tmin = 0;
    int ans = 0;
    for (int l = 0, r = 0; l < n; l++) {
        // [l,r),r永远是没有进入窗口的、下一个数所在的位置
        while (r < n && judge(arr[r])) {
            Push(r++);
        }
        ans = max(ans, r - l);
        Pop(l);
    }

    return ans;
}

int main() {
    cin >> n >> limit;
    for (int i = 0; i < n; i++)
        cin >> arr[i];
    int res = solve();
    cout << res << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值