2024/1/22 算法笔记

1.差分的应用

我们知道,差分的本质是一个数组前一个元素和后一个元素的差值的列表。

如果一个数组中每一个元素都相同,那么差分数组元素就都是0;

我们有一个问题是进行多次区间增值或减值后,至少要多少次,才能使数组中所有元素大小都相同。这里我们就可以应用上面的原则。

对原数组求差分数组,讲差分数组的负数元素绝对值加到一个sum1里,将正数元素的绝对值加到一个sum2 里,那么实际上我们的答案就是:ans = min(sum1 , sum2) + abs(sum1 - sum2)

另外一个问题是在最少次数的约束下,可以生成多少种不同的数组?(原数组处理过后的数组)

实际意义:

得到的数列有多少种,其实就是问的b[1]可以有多少种

我们上述所有操作是与b[1]无关的,因为我们的目标是让除了b[1]以外的项变0,所

以我们上述的操作没有考虑到b[1],b[1]怎么变,与我们求出的最小步骤无关

那么,我们怎么知道b[1]有几种呢?很简单,其实就是看看有几种一步步减或一步步

加的操作数,因为我们一步步加的时候(假设我们现在的操作对象下标为i),

可以这样操作,b[1]-1,b[i]+1 , 也就是说操作差分数组的时候总是从第一个元素开始到第i个元素。

一步步减的时候可以这样操作,b[1]+1,b[i]-1

(注意,一个差分序列里一步步操作的时候只可能一步步加或一步步减,不可能一步

步加和一步步减同时存在)

所以说,有几步一步步的操作就有几种情况+1,为什么+1呢,因为这个b[1]本身就有

一个值啊!就算你不对他进行任何操作,它自己也有一种情况。

一加一减(也就是我们所说的一步顶两步的操作)操作数为min(p,q)

那么一步步的操作数就为max(p,q)-min(p,q)=abs(p - q)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n,c,p,q,a[100010];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=2;i<=n;i++)
	{
		c=a[i]-a[i-1];
		if(c>0)
		{
			p+=c;
		}
		else 
		q-=c;
	}
	LL ans1=max(p,q);
	LL ans2=abs(p-q)+1;
	cout<<ans1<<endl<<ans2;
	return 0;
}

2.单调栈

维护一个栈,保证里面的元素按照一个固有的顺序排序。

[P2866 USACO06NOV] Bad Hair Day S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

维护方法是维护栈内从栈底到栈顶的元素大小逐渐减小,方法很显然是遇到大的将要放入栈中的元素,首先将站内比这个元素小的元素出栈。然后每放入一个元素就记录一次size,累计ans。

3. 双指针

又做了一个经典的滑动窗口的题

唯一的雪花 Unique Snowflakes - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

我们的任务使维护一个min值。

使队列里的元素都是独立唯一的。

事实上更应该称呼它为双指针

int a[1000005];
void solve(){
    int n;
    cin>>n;
    map<int,int> mp;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int ans = 0;
    int l=1,r=1;
    int cnt = 1;
    mp[a[1]]=1;
    while(r<n){
        r++;
        mp[a[r]]++;
        if(mp[a[r]]==1) {
            cnt++;
            continue;
        } 
        else{
            ans= max(ans,cnt);
            mp[a[l]]--;
            if(mp[a[l]]==0) cnt--;
            l++;
        }
    }
    cout<<ans<<endl;
}    //因为要用uva测,所以不太清楚结果如何,应该是对的。

更新于1.22:

[P3143 USACO16OPEN] Diamond Collector S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

也是一个双指针题目,先对钻石大小排序,对于每一个钻石,求符合要求的 左右两个柜子能放多少个钻石。

4.滑动窗口 (单调队列)

这次是真的单调队列题 虽然是模板题

给定一个固定大小的窗口,和一个数组,将这个窗口在数组种滑动,让其维护一些属性,比如窗口中的最大最小值,是否有重复元素等等。

具体做法是使用双端队列deque

结合代码来讲。这一份是求每走一步,窗口中的最大值的问题。

int a[1000005];
void solve(){
    int n,k;
    cin>>n>>k;
    deque<int>q;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        //当窗口左端到了需要删除的时候,删除元素直到大于等于i-k+1的部分
        while(!q.empty() && i-k+1>q.front()) q.pop_front();
   		//当队列右端元素小于当前要插入的元素的时候,我们就将这个下标删除掉。直到能将这个a[i]放入队列为止。
        while(!q.empty() && a[q.back()] < a[i]) q.pop_back();
        q.push_back(i);
        if(i>=k) cout<<a[q.front()]<<endl; //当i第一次到k的长度的时候,开始每次遍历输出一个最大值。因为比这个front小的都被删除了。
    }
}   

5. 其他

更新于1.22:

一个有意思的题,[P2671 NOIP2015 普及组] 求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

是分块分奇偶处理的一个问题,可以将复杂度降到O(n)

不过我想到了分块和分奇偶,没有推出求结果的公式,最后靠题解的公式过的,菜的抠脚

代码可以看提交记录(

另外,思路和公式的推导可以查看最后一篇题解,非常易懂,比前面几篇好。

另外一个是单调栈的问题,[P3467 POI2008] PLA-Postering - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

求最少能用多少个海报覆盖这些矩形组合。这个覆盖是可以跨楼覆盖的,按题意来看。

不考虑宽度,我们对于长度,有一个假设,

  1. 如果每一个楼的高度都不一样,我们就需要n张海报,也就是高楼数n。
  2. 如果有两座高度相同的高楼,我们就可以少用一张海报。
  3. 如果h( i ) > h( i + 1 ) ,我们就确定这个突出来的部分是一定要用到

相当于我们关心的是高度相同的楼能为我们节省多少张海报。(高度相同即可,不需要相邻),但是我们不能盖住空气,所以后一个h应该高于或等于前一个h。

象形的说就是凹形不能节省,凸形可以节省。

那么单调栈如何使用

void solve(){
    int n;
    cin>>n;
    stack<LL>s;
    LL num =0;
    for(int i=1;i<=n;i++){
        LL x,y;
        cin>>x>>y;
        //保证栈里面 的 高度都是递增的,这样,栈里面的元素,就是上升形的,就要用size张海报覆盖。
        while(!s.empty() && y<=s.top()){
            if(y == s.top()) num++;
            s.pop();
        }
        s.push(y);
    }
    cout<<n-num<<endl;
}

今天宿舍里只剩俺了,暴打2h农结果7连跪,直接卸载。
总之这几天先补题,玩游戏的事,等过年看看。

posted @ 2024-01-23 00:01  Akimizuss101  阅读(14)  评论(0编辑  收藏  举报