第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.砍竹子
优先队列+模拟不会