蓝桥杯一周突击

本文展示了多个编程问题的解决方案,包括进制转换的按权相加,通过枚举方法找出顺子日期,计算刷题统计的正确方式,模拟修剪灌木的规律,以及探讨X进制减法和统计子矩阵的问题。这些问题涉及到算法、数据结构和优化技巧,如暴力枚举、前缀和以及动态规划等。

第13届B组真题

1.进制转换

按权相加

#include<iostream>
using namespace std;
int main()
{
    cout<<2*9*9*9+0+2*9+2*1<<endl;
}

2.顺子日期

枚举

本题顺子的定义:i j k 是一个顺子,满足 i+1=j、j+1=k、i≥0。

枚举2022年的每一天,判定一下是不是顺子日期,是的话计数一下就好了。

int main()
{
	int arr[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};//明确每个月最多有几天,进行
	int a, b,c,d,e=0;
	for(a=0;a<2;a++)//月的十位
	{
		for (b = 0; b <= 9; b++)//月的个位
		{
			if ((a * 10 + b) <= 12)//保证月是合法的
			{
				for (c = 0; c <= 3; c++)//日的十位
				{
					for (d = 0; d <= 9; d++)//日的个位
					{
						if (arr[a * 10 + b] >= (c * 10 + d))//保证日是合法的
						{
							if (((a + 1) == b) && ((a + 2) == c))//判断前三个数是否合法
								e++;
							else
								if (((b + 1) == c)&&((b + 2) == d))//判断后三个数是否合法
								{
									e++;
								}
						}
					}
				}
			}
		}
	}printf("%d",e);
	return 0;
}

3.刷题统计

思路:先算周数然后枚举最后一周的情况,注意数据范围

第一次错误代码

#include<iostream>
using namespace std;
typedef long long LL;
int main(){
	LL a,b,n;
	cin>>a>>b>>n;
	LL week;
	week=n/(5*a+2*b);
	LL days;
	days=n%(5*a+2*b);
	LL res;
	res=week*7+days;
	cout<<res<<endl;
	return 0;
} 
//错误:没注意题给的数据范围,要开LONG LONG
//最后剩余题数思路不对 ,最后一周剩余题数分析的不对 

正确代码

#include<iostream>
using namespace std;
typedef long long LL;
int main(){
	LL a,b,n;
	cin>>a>>b>>n;
	
	LL s;
	s=5*a+2*b;//一周做题数可以设一个变量方便用,省事 
	LL res;
	res=n/s*7;//天数 
	n%=s;//不足一周的剩余题数 
	
	LL d[]={a,a,a,a,a,b,b};//最后一周开个数组 
	for(int i=0;n>0;i++)//n>0代表还有题没算 
	{
		n-=d[i];//减去题数天数加一 
		res++;
	}
	cout<<res<<endl;
	return 0;
} 

4.修剪灌木

模拟找规律

#include<iostream>
using namespace std;

int main(){
	int n;
	cin>>n;
	
	for(int i=1;i<=n;i++)
		cout<<max(2*(n-i),2*(i-1))<<endl;
		
	return 0;
} 

思维题

考虑当前修剪至第i个灌木,当前高度为0
1、如果当前向右修剪,那么经过i+1,i+2,⋅⋅⋅n−1,n后掉转方向,再经过n−1,n−2,⋅⋅⋅i+1, i回到第i个灌木,共经过2∗(n−i)次
2、如果当前向左修剪,那么经过i−1,i−2,⋅⋅⋅2,1后调转方向,再经过2,3⋅⋅⋅i−1,i回到第i个灌木,共经过2∗(i−1)次
那么最后的答案就是max(2(i−1),2(n−i))

5.X进制减法

不会,说是贪心

6.统计子矩阵

暴力前缀和试试

首先我们需要计算出二维前缀和,这里的方法就是简单的二维前缀和,算出来前缀和,我们就需要统计子矩阵里有多少个矩阵的大小小于等于K,这里的统计如果大家不会优化的话,就只能暴力循环,枚举子矩阵四个点进行四层暴力循环,这样可以过70%,但是还有30%过不掉,这到题目甚至还让你暴力骗分了

前缀和

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和。

加粗样式

二维前缀和

在这里插入图片描述

从图中我们很容易看出,整个外围蓝色矩形面积s[i][j] = 绿色面积s[i - 1][j] + 紫色面积s[i][j - 1] - 重复加的红色的面积s[i - 1][j - 1] + 小方块的面积a[i][j];

因此得出二维前缀和预处理公式

s[i][j] = s[i - 1][j] + s[i][j - 1 ] + a[i] [j] - s[i - 1][j - 1]
 

接下来回归问题求以(x1,y1)为左上角和以(x2,y2)为右下角的矩阵的元素的和。

 

紫色面积是指 (1, 1)左上角到(x1 - 1, y2)右下角的矩形面积 ,黄色面积是指(1, 1)左上角到(x2, y1 - 1)右下角的矩形面积

不难推出:

在这里插入图片描述

绿色矩形的面积 = 整个外围面积s[x2, y2] - 黄色面积s[x2, y1 - 1] - 紫色面积s[x1 - 1, y2] + 重复减去的红色面积 s[x1 - 1, y1 - 1]

因此二维前缀和的结论为:

以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1, y1 - 1]
总结:

 

练习一道完整题目:
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式
第一行包含三个整数n,m,q。

接下来n行,每行包含m个整数,表示整数矩阵。

接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。

输出格式

共q行,每行输出一个询问的结果。

数据范围
 

1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000

 输入样例:

3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

 输出样例:

17
27
21

代码:

#include <iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int s[N][N];
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }
    return 0;
}

本题具体解法:

四层for暴力解法 O(N4)(70分)

枚举每个点为起点,和枚举每个点为终点,这样就可以得到所有的子矩阵(不重不漏),但时间复杂度是O(n^4),会超时;

//暴力做法
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
int n, m, k;
typedef long long ll;
int s[N][N];
ll res;

int main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			cin >> s[i][j];
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
		}
	for (int x1 = 1; x1 <= n; x1++)
		for (int y1 = 1; y1 <= m; y1++)
			for (int x2 = x1; x2 <= n; x2++)
				for (int y2 = y1; y2 <= m; y2++) {
					int sum = s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
					if (sum <= k)
						res++;
				}
	cout << res << endl;
	return 0;
}

自己写的好像不对(待检查)

#include<iostream>
using namespace std;
typedef long long LL;
const int N=510;
int n,m,k;
int s[N][N];
LL res;

int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			cin>>s[i][j];
			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
		}	
	for(int x1=1;x1<=n;x1++)
		for(int y1=1;y1<=m;y1++)
			for(int x2=x1;x2<=n;x2++)
				for(int y2=y1;y2<=m;y2++){
					int sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
					if(sum<=k)
						res++;	
				}
	cout<<res<<endl;
	return 0;							
}

j<=m没写等于

 

双指针可以优化(不会)

7.积木画

dp,一层层推找规律

蒙德里安的梦想(状态压缩dp经典模板题)

8.扫雷

dfs图论不会

9.李白打酒加强版

dp不会

10.砍竹子

优先队列+模拟不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值