2022.1.19 解题报告

解题报告

1.特殊数字

题目描述:

https://www.acwing.com/problem/content/3550/

思路:

由于数据很小,那么直接从n往后暴力枚举即可。

代码:

#include<iostream>
using namespace std;
int n;
int main(){
    scanf("%d",&n);
    for(int i = n ;  ; i ++ ){
        int x = i,num = 0; //num表示x各个位数上的数字之和
        while(x){
            num += x % 10;
            x /= 10;
        }
        if(num % 4 == 0){ //判断一下x的各位数字之和是否满足题目所给性质
            printf("%d\n",i);
            return 0;
        }
    }
}

2.双端队列

题目描述:

https://www.acwing.com/problem/content/3551/

思路:

注意到数据中q不超过200000,所以我们可以将整个数轴向右平移200005个单位长度,这样就可以直接在数组两端加数字了。

所以声明一个变量mid = 200005,作为平移后的数轴“原点”。

那么,这题再用两个双指针l,r分别更新”原点“左右两端的数字就可以完成前两种操作了。

那么对于第三种操作,在更新的过程中,我们还需要用一个i数组来记录每一个数字相对于“原点”的偏移量,以便查询。

所以,在查询时,我们只需要计算出l和r目前相对“原点”的偏移量,再取一个小的输出就是答案了。

注意:

在更新数组的过程中,l和r都不从“原点”开始。这意味着整个数组中的数并不是完全连续的,在下标为-1和1的两数之间,还存在一个“原点”。

所以,在最后计算时,还要判断查询的x在“原点”的左侧还是右侧(即判断i[x]的正负性),最后再输出。

代码:

#include<iostream>
using namespace std;
const int N = 4e5 + 10,mid = N >> 1; //开题目数据的两倍
int a[N],i[N];
int q;
int main(){
	scanf("%d",&q);
	char op[2];
	int x;
	int l = mid - 1,r = mid + 1;
	while(q -- ){
		scanf("%s%d",op,&x);
		if(*op == 'L'){
			a[l -- ] = x;
			i[x] = l - mid + 1; //储存偏移量
		}
		else if(*op == 'R'){
				a[r ++ ] = x;
				i[x] = r - mid - 1; //储存偏移量
			}
			else{
				if(i[x] < 0) //判断正负性
				    printf("%d\n",min(r - 2 - i[x] - mid,i[x] - l - 1 + mid));
				else
				    printf("%d\n",min(r - 1 - i[x] - mid,i[x] - l - 2 + mid));
			}
	}
	return 0;
}

3.最长非递减子序列

题目描述:

https://www.acwing.com/problem/content/description/3552/

思路:

由样例“1 2 1 2”拓展可得到序列“1 … 1 2 … 2 1 … 1 2 … 2”在一次反转后也可以变为长度为n的不下降子序列,即全部选中。

所以,将上面这中一段1,一段2,一段1,一段2的序列,记为“好序列”。

那么,问题即可转化为:

求不反转原序列时的最大子“好序列”长度。

又因为在一个“好序列”中,第一段1和第一段2恰好为一段不下降子序列,第二段1和第二段2也是;

所以我们可以预处理两个数组pre和nxt,其中pre[i]表示1i中的最长不下降子序列长,nxt[i]表示i+1n中的最长不下降子序列长。

那么显然,答案就是pre[i] + nxt[i + 1]的最大值。

代码:

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int n,a[N],pre[N],nxt[N];
int f[2]; //DP数组
int main(){
	scanf("%d",&n);
    for(int i = 1 ; i <= n ; i ++ ){
        scanf("%d",a + i);
        a[i] -- ; //将1,2变为0,1
    }
    
    f[0] = f[1] = 0;
    for(int i = 1 ; i <= n ; i ++ ) //预处理pre数组
        if(!a[i]) pre[i] =  ++f[0]; //如果当前这一位是0(即原本的1)
        else pre[i] = f[1] = max(f[1],f[0]) + 1;
    
    f[0] = f[1] = 0;
    for(int i = n ; i ; i -- )
        if(a[i]) nxt = ++f[1];
        else nxt[i] = f[0] = max(f[0],f[1]) + 1;
    
    int res = 0;
    for(int i = 1 ; i <= n ; i ++ )
        res = max(res,pre[i] + nxt[i + 1]);
   	printf("%d\n",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值