从零开始的蓝桥杯(三)

目录

一.行列找规律问题

1.Excel的列

在 Excel 中,列的名称使用英文字母的组合。前 26 列用一个字母,依次为 A 到 Z,接下来 26*26 列使用两个字母的组合,依次为 AA 到 ZZ,依次循环。

请问第 2022 列的名称是什么?

#include <iostream>

using namespace std;

int main()
{
    int i,j;
    int n = 2022%(26+26*26);
    for(i=0;i<26;i++)
        for(j=0;j<26;j++)
            if((i+1)*26+j+1 == n-26) printf("%c%c",65+i%26, 65+j%26);
    return 0;
}

技巧:对于存在循环的情况,要求第m个数是什么,先算出一个循环内的个数n,再进行m%n运算,然后用这个结果再去遍历即可。

变式

在 Excel 中,列的名称使用英文字母的组合。前 26 列用一个字母,依次为 A 到 Z,接下来 26*26 列使用两个字母的组合,依次为 AA 到 ZZ,然后是AAA到ZZZ,依此类推。

请问第 2022 列的名称是什么?

(其实就相当与转化为26进制了,只是都用字母来表示了)

#include <iostream>

using namespace std;

int main()
{
    int n = 2022;
    int arr[10]={0};
    int i = 0;
    while(n>0){
        arr[i++] = n%26;
        n /= 26;
    }
    for(int j = i-1;j>=0;j--) printf("%c",arr[j]+65-1);
    return 0;
}

这里arr【 j 】+65还要减去1,是因为这里下标是从1到26 ,而不是从0到25

2.最小子矩阵

小蓝有一个 100 行 100 列的矩阵,矩阵的左上角为 1。其它每个位置正好比其左边的数大 2,比其

上边的数大 1 。例如,第 1 行第 2 列为 3,第 2 行第 2 列 为 4,第 10 行第 20 列为 48。

小蓝想在矩阵中找到一个由连续的若干行、连续的若干列组成的子矩阵,使得其和为 2022

请问这个子矩阵中至少包含多少个元素(即子矩阵的行数和列数的乘积)
 

#include <iostream>

using namespace std;

int main()
{
    int s[100][100] = {1};
    for(int j = 0; j<100; j++)
        s[0][j] = 2*j+1;

    for(int i = 1; i<100; i++)
        for(int j = 0; j<100; j++)
            s[i][j] = s[i-1][j]+1;

    int sum = 0;
    int temp = 10000;
    for(int i1 = 0; i1<100; i1++)
        for(int i2 = i1; i2<100; i2++)
            for(int j1 = 0; j1<100; j1++)
                for(int j2 = j1; j2<100; j2++)
                {
                    for(int i = i1; i<=i2; i++)
                        for(int j = j1; j<=j2; j++)
                        {
                            sum += s[i][j];
                            if(sum==2022)
                            {
                                temp = min(temp,(i2-i1+1)*(j2-j1+1));
                                goto Hello;
                            }
                            if(sum>2022)
                                goto Hello;
                        }
Hello:
                    sum = 0;
                }

    cout<<temp;
    return 0;
}
//Wrong Answer: 10
//Correct Answer: 12

正解:

#include<iostream>
#include <vector>
using namespace std;
typedef long long ll;
vector<vector<int> > adj(100);
 
int inline sum(int a, int b, int c, int d) 
{
    int res = 0;
    for (int i = a; i < a + c; i++) 
	{
        for (int j = b; j < b + d; j++) 
		{
            res += adj[i][j];
        }
    }
    return res;
}
 
int main() 
{
    for (int i = 0; i < 100; i++) 
	{
        adj[i].push_back(i + 1);
        for (int j = 1; j < 100; j++) 
		{
            adj[i].push_back(adj[i][j - 1] + 2);
        }
    }
    int ans = 10001;
    for (int i = 0; i < 100; i++) 
        for (int j = 0; j < 100; j++) 
            for (int k = 1; k <= 100 - i; k++)
                for (int l = 1; l <= 100 - j; l++) 
                    if (sum(i, j, k, l) == 2022) 
					{
                        ans = min(ans, k * l);
                        break;
                    } 
					else if (sum(i, j, k, l) > 2022) 
					break;
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

二.日期问题

1. 相等日期


问题描述
对于一个日期,我们可以计算出年份的各个数位上的数字之和,也可以分别计算月和日的各位数字之和。请问从 1900 年 1 月 1 日至 9999 年 12 月 31 日,总共有多少天,年份的数位数字之和等于月的数位数字之和加日的数位数字之和。
例如,2022年11月13日满足要求,因为 2+0+2+2=(1+1)+(1+3) 。

请提交满足条件的日期的总数量。

#include <iostream>

using namespace std;

int mon[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

int Judge(int y){
    return (y%4==0 && y%100!=0) || y%400==0;
}
int fun(int n){
    int sum = 0;
    while(n>0){
        sum += n%10;
        n /= 10;
    }
    return sum;
}
int main()
{
    int y,m,d;
    int cnt= 0;
    for(y = 1900;y<10000;y++){
        if(Judge(y)) mon[1] = 29;
        else mon[1] = 28;
        for(m = 1;m<13;m++){
            for(d = 1;d<mon[m-1]+1;d++){
                if(fun(y) == fun(m)+fun(d)) cnt++;
            }
        }
    }
    cout<<cnt;
    return 0;
}

//Answer: 70910

坑:if(Judge(y)) mon[1] = 29;
        else mon[1] = 28;

不能写成 mon[1] += Judge(y) ;因为这样就实现不了重置mon[1] 为28了

2.晨跑小蓝

  小蓝每周六、周日都晨跑,每月的 1、11、21、31日也晨跑。其它时间不晨跑。
  已知 2022年1月1日是周六,请问小蓝整个2022年晨跑多少天?

// 像这种会有重复的可以定义一个365天的数组,这样反复赋值也不会影响(有点像flood fill)

#include <iostream>

using namespace std;

int month[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

int main()
{
    int cnt = 0;
    int arr[366] = {0};

    int i;
    for(i = 0; i*7+2<=365; i++)
    {
        arr[i*7+1] = 1;
        arr[i*7+2] = 1;
    }
    if(i*7+1<=365) arr[i*7+1] = 1;

    int d1,d2,d3,d4;
    d1 = 1,d2 = 11,d3 = 21,d4 = 31;

    for(int i = 0; i<12; i++)
    {
        arr[d1] = 1;
        arr[d2] = 1;
        arr[d3] = 1;
        if(month[i]==31)
            arr[d4] = 1;
        d1+=month[i];
        d2+=month[i];
        d3+=month[i];
        d4+=month[i];
    }

    for(int i = 1; i<=365; i++)
        if(arr[i]) cnt++;

    cout<<cnt;

    return 0;
}
// Answer: 138

优化:

#include<iostream>
using namespace std;
 
int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
int main() 
{
    int ans = 0;
    int week = 6;
    for (int month = 1; month <= 12; month ++) 
    {
        for (int day = 1; day <= days[month]; day ++) 
        {
            ans += (week == 6 || week == 0 || day % 10 == 1);
            week = (week + 1) % 7;
        }
    }
    cout << ans << endl;
    return 0;
}

三.暴力枚举法

1.小蓝的数
 

 小蓝有 30 个数,分别为:99, 22, 51, 63, 72, 61, 20, 88, 40, 21, 63, 30, 11, 18, 99, 12, 93, 16, 7, 53, 64, 9, 28, 84, 34, 96, 52, 82, 51, 77 。
  小蓝可以在这些数中取出两个序号不同的数,共有 30*29/2=435 种取法。
  请问这 435 种取法中,有多少种取法取出的两个数的乘积大于等于 2022 。
答案提交
  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
 错解:

#include <iostream>

using namespace std;

int main()
{
    int cnt = 0;
    int arr[30];
    for(int i = 0;i<30;i++){
         scanf("%d",&arr[i]);
         getchar();
    }
    for(int i = 0;i<30;i++)
        for(int j = 0;j<30;j++)
            if(arr[i]*arr[j]>=2022) cnt++;
    cout<<cnt;
    return 0;
}

错误原因:

1.435种取法,而用的嵌套循环多弄了一倍(就相当于对称矩阵,只要求计算下三角即可)

修正:内循环约束条件改为 j <= i 即可

2.序号不同的数!!!而这里完全没注意到这个限制了

修正:改为 j < i (也就是下三角出去对角线了)

经验:

对于99, 22, 51, 63, 72, 61, 20, 88, 40, 21, 63, 30, 11, 18, 99, 12, 93, 16, 7, 53, 64, 9, 28, 84, 34, 96, 52, 82, 51, 77 这种不是由空格隔开的数据输入

可以用 scanf("%d",&arr[i]); 加一个 getchar(); 来实现捏!!! 
 正解:

#include <iostream>

using namespace std;

int main()
{
    int cnt = 0;
    int arr[30];
    for(int i = 0;i<30;i++){
         scanf("%d",&arr[i]);
         getchar();
    }
    for(int i = 0;i<30;i++)
        for(int j = 0;j<i;j++)
            if(arr[i]*arr[j]>=2022) cnt++;
    cout<<cnt;
    return 0;
}
//Answer: 189

2.区间最小值

小蓝有一个序列 a[1], a[2], …, a[n]。

给定一个正整数 k,请问对于每一个 1 到 n 之间的序号 i,a[i-k], a[i-k+1], …, a[i+k] 这 2k+1 个数中的最小值是多少?当某个下标超过 1 到 n 的范围时,数不存在,求最小值时只取存在的那些值。

输入格式

  输入的第一行包含一整数 n。

  第二行包含 n 个整数,分别表示 a[1], a[2], …, a[n]。

  第三行包含一个整数 k 。

输出格式

  输出一行,包含 n 个整数,分别表示对于每个序号求得的最小值。

样例输入

5 2 7 4 3 

样例输出

2 2 2 3 3
 

#include <iostream>

using namespace std;


int main()
{
    int n;
    cin>>n;

    int* arr = new int[n+1]();
    for(int i=1; i<=n; i++)
        cin>>arr[i];

    int k;
    cin>>k;

    for(int i=1; i<=n; i++)
    {
        int left = i-k;
        if(left<1) left = 1;

        int right = i+k;
        if(right>n) right = n;

        int minimum = left;

        for(int j = left+1; j<=right; j++)
        {
            if(arr[minimum]>arr[j]) minimum = j;
        }
        cout<<arr[minimum]<<" ";
    }

    return 0;
}

可能会超时, 可以使用:单调队列、线段树、动态规划、记忆搜索等等

3.箬治的调和级数

小蓝特别喜欢调和级数 S(n)=1/1+1/2+1/3+1/4+...+1/n 。
  请问,n 至少为多大时,S(n)>12 ?

#include <iostream>

using namespace std;

int main()
{
    double sum = 0;
    double n = 1;
    while(sum<12)
    {
        sum += 1/n++;
    }
    cout<<n;
    return 0;
}
// Wrong Answer: 91381
// Correct Answer: 91380

这里怎么及时发现错误呢:直接把 12 改成 1 和 1.5 看看输出

改为1时输出3,但其实应该输出2;改为1.5时输出4,但应该输出3

故答案每次都多了1,就出问题在n++这里,会多加一次(改成 int n=0,++n 就可以了)

#include <iostream>

using namespace std;

int main()
{
    double sum = 0;
    double n = 0;
    while(sum<=12)
    {
        sum += 1/++n;
    }
    cout<<n;
    return 0;
}
// Wrong Answer: 91381
// Correct Answer: 91380

4.爱跑步的小明


小明要做一个跑步训练。 初始时,小明充满体力,体力值计为 10000。如果小明跑步,每分钟损耗600的体力。如果小明休息,每分钟增加 300 的体力。体力的损耗和增加都是均匀变化的。
小明打算跑一分钟、休息一分钟、再跑一分钟、再休息一分钟……如此循环。如果某个时刻小明的体力到达0,他就停止锻炼。 请问小明在多久后停止锻炼。
为了使答案为整数,请以秒为单位输出答案。 答案中只填写数,不填写单位。
 

#include <iostream>

using namespace std;

int main()
{
    int time = 0;
    int m = 10000;
    while(m>600)
    {
        m -= 600;
        time += 60;
        m += 300;
        time += 60;
    }
    time += m/10;
    cout<<time;
    return 0;
}
//Answer: 3880

5.分配口罩

在这里插入图片描述
【解题】
这个题就是把数组分成两个部分,求两部分和最小值的包装版本

可以用暴力dfs 也可以dp(0-1背包作背包容量为sum/2的dp),结果填空且只有15个数,直接暴力搜即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<math.h>

using namespace std;
typedef long long ll;

ll num[15]={9090400,8499400,5926800,8547000,4958200,4422600,5751200,4175600,6309600,5865200,6604400,4635000,10663400,8087200,4554000};

ll minn=0x3f3f3f3f;

ll sum;

void dfs(int pos,ll now)
{
	if(pos>=15)
	{
		minn=min(minn,abs(sum-2*now));
		return ;
	}

	dfs(pos+1,now+num[pos]);//选

	dfs(pos+1,now); //不选

}

int main()
{
	int i;
	sum=0;
	for(i=0;i<15;i++)
	{
		sum+=num[i];
	}
	dfs(0,0);
	cout<<minn;
	return 0;
 }

 //ans==2400;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值