劳役结合的第一天

菜死我了

我是废物

我是厕所清道夫

期末考试刚过去要看文化课成绩T人,理综+语数外全校得前500,最后算是带着全校800+的文综考了总分511,老师也没说要不要T掉我,但总归庆幸自己时留下来了。

学了很多

但是两天只写了9题,其中磕的时间最长的是一道单调队列,是从昨天下午就写,写了一下午,晚上回家又看了看,真改不出来。

在洛谷P1886.

有一个长为 nn 的序列 aa,以及一个大小为 kk 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值在这里插入图片描述
一开始手想写队列但是没写队尾,只得了30分,而且代码太乱太长太丑陋,所以改了好长时间都改不出来。

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+2;
int a[N],dd[N],xx[N],xiaokongzhi,xiao,da,dakongzhi,n,k;//dakongzhi和xiaokongzhi分别代表最大最小控制范围
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
	for(int i=1;i<=k;i++)
	{
		if(a[i]>=da)
		{
			da=a[i];
			dakongzhi=i+k-1;
		}
		if(a[i]<=xiao)
		{
			xiao=a[i];
			xiaokongzhi=i+k-1;
		}
	}
	dd[1]=da;xx[1]=xiao;//dd表示最大数,xx表示窗口的最小数
	for(int i=2;i<=n;i++)
	{
		if(i<=dakongzhi)//如果新的数还在之前最大数的控制范围内
		{
			if(dd[i-1]<=a[i+k-1])//判断窗口右端点的数(即新进窗口的数)是否大于先在窗口的最大数
			{
				dd[i]=a[i+k-1];
				dakongzhi=i+k-1;//跟新最新的大数的控制范围
			}
			else dd[i]=dd[i-1];//否则还在原来的控制之下
		}
	else	if(i>dakongzhi)//如果超出其控制范围
		{
			da=0;
			for(int j=i;j>=i+k-1;j++)//扫一遍新的窗口范围内的最大数
			{
				if(da<=a[j])
				{
					da=a[j];
					dakongzhi=j+k-1;
				}
			}
			dd[i]=da;
		}
		if(i<=xiaokongzhi)
		{
			if(xx[i-1]>=a[i+k-1])
			{
				xx[i]=a[i+k-1];
				xiaokongzhi=i+k-1;
			}
			else xx[i]=xx[i-1];
		}
	else	if(i>xiaokongzhi)
		{
			xiao=9999999;
			for(int j=i;j<=i+k-1;j++)
			{
				if(xiao>=a[j])
				{
					xiao=a[j];
					xiaokongzhi=k+j-1;
				}	
			}
			xx[i]=xiao;
		}
	}
	cout<<xx[1];
	for(int i=2;i<=n-k+1;i++)
	{
		cout<<' '<<xx[i];
	}
	cout<<endl;
	cout<<dd[1];
	for(int i=2;i<=n-k+1;i++)
	{
		cout<<' '<<dd[i];
	}
}

首先吐槽下校oj这道题行末不能有空格。
我写完这个感觉最坏打算也就是O(n*k),确实可能超时,但是不知道为啥才30分,然后死活搞不出来,第二天(也就是今天早上),照着这个思路重构一遍,每次更新窗口里的次小和其控制范围,算是优化了不少,按说写出来是可能A掉的,但是代码比之前写的还乱,写到一截自闭了。
在这里插入图片描述真乾啊!
所以我决定对自己好亿点,换个思路。

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

bool flag;
const int N=1e6+10;
int n,k,a[N];
struct chuanghu{
	int daxiao,name;
}c[N];

void work()
{
	int head=1,tail=0;
	for(int i=1;i<k;i++)
	{
		while(a[i]<c[tail].daxiao&&tail>0)    tail--;
		c[++tail].daxiao=a[i],c[tail].name=i;
	}
	for(int i=k;i<=n;i++)
	{
		while(a[i]<c[tail].daxiao&&tail>=head)	tail--;
		c[++tail].daxiao=a[i],c[tail].name=i;
		while(c[head].name+k<=i&&head<=tail)    head++;
		if(i!=n&&flag==0)	printf("%d ",c[head].daxiao);
		if(i!=n&&flag==1)	printf("%d ",c[head].daxiao*-1);
	}
	if(!flag)	printf("%d",c[head].daxiao);
	else	printf("%d",c[head].daxiao*-1);
}

int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
	work();
	flag=1;
	puts("");
	for(int i=1;i<=n;i++)	a[i]*=-1;
	work();
	return 0;
}

这次的队列算是有头有尾了,然后写一个函数输出每次移动窗口的最小值,输出完把flag立为1,再把所有数乘-1,接着在调用刚刚输出最小值的函数,但是这次输出时乘个-1把数乘回来。我可真是个小机灵鬼,两个愿望一次满足
在这里插入图片描述
所以这种可能需要用到两次或以上的工作最好还是搞个函数。
而在大佬lyn.看到我A了这道题后还让我帮忙看了代码,用的vector,我这个不会vector的小菜鸡算是现学了一遍,(最后还是大佬自己改出来的)不过她那个A了表示我一开始的思路要是能写完也能A。
然后还有一个比较印象深的就是

地平线

题目描述

Farmer John的牛们认为,太阳升起的那一刻是一天中最美好的,在那时她们 可以看到远方城市模糊的轮廓。显然,这些轮廓其实是城市里建筑物模糊的影子。   建筑物的影子实在太模糊了,牛们只好把它们近似地看成若干个边长为1单位 长度的正方体整齐地叠在一起。城市中的所有建筑物的影子都是标准的矩形。牛们 的视野宽W个单位长度(1<=W<=1,000,000),不妨把它们按从左到右划分成W列,并 按1~W编号。建筑物的轮廓用N组(1<=N<=50,000)数给予描述,每组数包含2个整数 x、y(1<=x<=W,0<=y<=500,000),表示从第x列开始,建筑物影子的高度变成了y。 (也就是说,第x[i]列到第x[i+1]-1列中每一列建筑物影子的高度都是y[i]个单位 长度)

贝茜想知道这座城市里最少有多少幢建筑物,也就是说,这些影子最少可以由 多少个矩形完全覆盖。当然,建筑物的影子可以有重叠。请你写一个程序帮她计算 一下。

输入格式

第1行: 2个用空格隔开的整数,N和W
第2…N+1行: 每行包括2个用空格隔开的整数x、y,其意义如题中所述。输入中的 x 严格递增,并且第一个x总是1。

输出格式

第1行: 输出一个整数,表示城市中最少包含的建筑物数量

样例数据

input

10 26
1 1
2 2
5 1
6 3
8 1
11 0
15 2
17 3
20 2
22 1

输入说明:

样例对应题中的那个图

output

6

一开始还是不知道该往哪想,但是后来领悟到了“正难则反”

先让每个建筑物自成一个矩形,然后判断有多少能合并,就删掉几个,然后输出。

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int h[N],n,w,meiyong,t,y,ans;
int main()
{
	cin>>n>>w;
	t++;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&meiyong,&y);
		while(t&&h[t]>=y)
		{
			if(h[t]==y) ans++;
			t--;
		}
		t++;
		h[t]=y;
	}
	cout<<n-ans;
	return 0;
}

“正难则反”真香!!!

然后剩下的就都是些Hash表,字符串Hash和KMP的题,大多都是听别人讲讲,看看书,看看别人的代码,才勉强水过的几道板子题。感觉还是没消化,有几个看着玄乎得堪比DP,所以这里不多写了,还是得多看看书做做题刷刷CSDN。

主要还是因为我太菜了QAQ

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值