acwing蓝桥杯

Posted meteox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了acwing蓝桥杯相关的知识,希望对你有一定的参考价值。

写在前面

距离这届蓝桥杯已经过了八个月了,一直没总结算法,实在是太懒了,懒狗一条…

考前看知乎上说蓝桥杯一个月准备可以拿省一,当时还真信了,不过也可能是我有点懒,准备也不够一个月,没有专门投入太多,刷的题也不多,后面快考的时候直接摆烂了,觉得可能没啥希望,就随便看看真题了,最后就拿了个江苏CA省二,排位大概省二中上一点的样子,聊胜于无吧,没啥意思,我只想说,***退钱!!!

如果没啥竞赛基础的同学建议先看一下胡凡的《算法笔记》,这本书挺适合入门的,可以了解到算法竞赛中的大致考点,看完后就可以直接对应刷题了,如果时间不是很充足的话我个人不是很建议看紫书蓝书这种纯竞赛书,会花很多时间,而且考点是不太一样的,建议就刷acwing的蓝桥杯专题,然后多刷真题,真题就已经很多了,真题可以到官网上找,或者去一些oj也行,当时还看了罗老师的蓝桥杯每日一题https://blog.csdn.net/weixin_43914593/category_10721247.html挺不错的。不建议单纯刷力扣来准备,力扣上的用stl比较多,竞赛上更多的是用数组模拟,而且两者的侧重不同,力扣上更侧重解决的方法,只需要写solve函数就行,算法竞赛还需要自己注意输入输出格式的转换等等,当然用它复习知识点是可以的。

之前蓝桥杯搜索题很多,也“暴力杯”,但现在也越来越多动态规划题了,感觉蓝桥杯越来越难了,不太能随便混奖了,这就是内卷的结果吗…

蓝桥杯c++是可以用万能头的,而且看样例通过数给分,可以打表或者暴力骗点分,该骗的时候一定要骗,不要硬磕,也不要啥都不写(大佬直接忽略),写题的时候可以记录下思路整理到笔记,也方便以后看看,我就没写,所以下面题解都没啥讲解思路,刷题途中可能会遇到一些库的用法不太熟悉,或者一些转换的骚操作一定要单独记到笔记上面,方便复习查看。

这种编程算法还是要多敲的,一段时间不敲连二分都敲不利索,而且还有一些模板题啥的也要靠理解记忆,太长时间不敲是真不太行,手感还是很重要的,怎么说呢,个人还是更喜欢ctf和ai这种竞赛,可以翻阅笔记在线搜索啥的,不用在死板的东西上花太多时间去记忆。

明年的蓝桥杯本来也是打算报一下的,但想到四五月份可能准备考研了,时间不太充裕,这种算法又太依赖刷题和当时手感,不如就打打ctf算了。当然还有一个重要原因,300报名费还是太贵了…

第一讲 递归与递推

92. 递归实现指数型枚举

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式

输入一个整数 n。

输出格式

每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围

1≤n≤15

输入样例:

3

输出样例:

3
2
2 3
1
1 3
1 2
1 2 3
#include<bits/stdc++.h>
using namespace std;
bool st[20];
int i,n;
void dfs(int x)
    if(x>n)
        for(i=1;i<=n;i++)
            if(st[i])
                cout<<i<<" ";
            
        
        cout<<endl;
        return;
    

    st[x]=true;
    dfs(x+1);
    st[x]=false;
    dfs(x+1);

int main()
    cin>>n;
    dfs(1);
    return 0;

94. 递归实现排列型枚举

把 1∼n1∼n 这 nn 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式

一个整数 nn。

输出格式

按照从小到大的顺序输出所有方案,每行 11 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围

1≤n≤91≤n≤9

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include<bits/stdc++.h>
using namespace std;
int vis[20],book[20],n;
void dfs(int x)
    if(x>n)
        for(int i=1;i<=n;i++) 
            cout<<book[i]<<" ";
            
        
        cout<<endl;
        return;
    
        for(int i=1;i<=n;i++)
            if(vis[i]==0)
                book[x]=i;
                vis[i]=1;
                dfs(x+1);
                vis[i]=0;
            
        
        
    
    

int main()
    cin>>n;
    dfs(1);
    return 0;

717. 简单斐波那契

以下数列 0 1 1 2 3 5 8 13 21 ... 被称为斐波纳契数列。

这个数列从第 33 项开始,每一项都等于前两项之和。

输入一个整数 N,请你输出这个序列的前 N 项。

输入格式

一个整数 N。

输出格式

在一行中输出斐波那契数列的前 N 项,数字之间用空格隔开。

数据范围

0<N<46

输入样例:

5

输出样例:

0 1 1 2 3
//递归带剪枝(备忘录)
#include<bits/stdc++.h>
using namespace std;
int N[50];

int fib(int n)
    if(N[n]) return N[n];
    if(n==0) return 0;
    if(n==1||n==2) return 1;
    N[n]=fib(n-1)+fib(n-2);
    return N[n];

int main()
    int n;
    N[0]=0,N[1]=1,N[2]=1;
    cin>>n;
    fib(n-1);
    for(int i=0;i<n;i++) cout<<N[i]<<" ";
    return 0;


//不用数组直接输出
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int main()

    int a = 0, b = 1;
    int n;
    cin >> n;

    for (int i = 0; i < n; i ++ )
    
        cout << a << ' ';
        int c = a + b;
        a = b, b = c;
    

    cout << endl;

    return 0;

95. 费解的开关

你玩过“拉灯”游戏吗?

25 盏灯排成一个 5×5 的方形。

每一个灯都有一个开关,游戏者可以改变它的状态。

每一步,游戏者可以改变某一个灯的状态。

游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字 11 表示一盏开着的灯,用数字 00 表示关着的灯。

下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在 66 步以内使所有的灯都变亮。

输入格式

第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。

以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。

每组数据描述了一个游戏的初始状态。

各组数据间用一个空行分隔。

输出格式

一共输出 n 行数据,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。

对于某一个游戏初始状态,若 66步以内无法使所有灯变亮,则输出 −1。

数据范围

0<n≤5000

输入样例:

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

输出样例:

3
2
-1
//二进制模拟枚举    本行的状态都通过下一行的改变而改变

#include<bits/stdc++.h>
using namespace std;
const int N = 6;

char g[N][N], backup[N][N];
int dx[5] = -1, 0, 1, 0, 0, dy[5] = 0, 1, 0, -1, 0;

void turn(int x, int y)

    for (int i = 0; i < 5; i ++ )
    
        int a = x + dx[i], b = y + dy[i];
        if (a < 0 || a >= 5 || b < 0 || b >= 5) continue;   // 在边界外,直接忽略即可
        g[a][b] ^= 1;//异或,同0异1,与1异或则状态改变
    


int main()
    int T;
    cin >> T;
    while (T -- )
    
        for (int i = 0; i < 5; i ++ ) cin >> g[i];

        int res = 10;
        for (int op = 0; op < 32; op ++ ) //32为第一行所有不同的按法,
        
            memcpy(backup, g, sizeof g);
            int step = 0;
            for (int i = 0; i < 5; i ++ )
                if (op >> i & 1)//
                
                    step ++ ;
                    turn(0, i);
                

            for (int i = 0; i < 4; i ++ )
                for (int j = 0; j < 5; j ++ )
                    if (g[i][j] == '0')
                    
                        step ++ ;
                        turn(i + 1, j);//按同列的下一行即可将此处的0变为1
                    

            bool dark = false;
            for (int i = 0; i < 5; i ++ )//如果最后一行全是1则成功
                if (g[4][i] == '0')
                
                    dark = true;
                    break;
                

            if (!dark) res = min(res, step);
            memcpy(g, backup, sizeof g);
        

        if (res > 6) res = -1;

        cout << res << endl;
    

    return 0;


93. 递归实现组合型枚举

从 1∼n这 n 个整数中随机选出 m 个,输出所有可能的选择方案。

输入格式

两个整数 n,m ,在同一行用空格隔开。

输出格式

按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。

数据范围

n>0
0≤m≤n,
n+(n−m)≤25

输入样例:

5 3

输出样例:

1 2 3 
1 2 4 
1 2 5 
1 3 4 
1 3 5 
1 4 5 
2 3 4 
2 3 5 
2 4 5 
3 4 5 

思考题:如果要求使用非递归方法,该怎么做呢?

#include<bits/stdc++.h>
using namespace std;
int res[20];
int a,b;
void dfs(int max,int num,int n,int a)
    if(a==num) 
        for(int x=0;x<num;x++)
            cout<<res[x]<<" ";
        
        cout<<endl;
        return;
    
    for(int i=n;i<=max;i++)
        res[a]=i;
        dfs(max,num,i+1,a+1);
    

int main()
    
    cin>>a>>b;
    dfs(a,b,1,0);
    return 0;

1209. 带分数

100100 可以表示为带分数的形式:100=3+69258/714

还可以表示为:100=82+3546/197

注意特征:带分数中,数字 1∼91∼9 分别出现且只出现一次(不包含 00)。

类似这样的带分数,100100 有 1111 种表示法。

输入格式

一个正整数。

输出格式

输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1≤N<106

输入样例1:

100

输出样例1:

11

输入样例2:

105

输出样例2:

6
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int num[N];
bool used[N];
int target;
int cnt=0;
int calc(int l,int r)
    int res=0;
    for(int i=l;i<=r;i++)
        res=res*10+num[i];
    
    return res;

void dfs(int s)
    if(s==9)
        for(int i=1;i<=6;i++)
            for(int j=i+1;j<=8;j++)
                int a = calc(1, i);
                int b = calc(i + 1, j);
                int c = calc(j + 1, 9);
                if(a * c + b == c * target) cnt++;
            
            return;
    
    for(int i=1;i<10;i++)
        if(!used[i])
            used[i]=true;
            num[s+1]=i;
            dfs(s+1);
            used[i]=false;
        
        
        
    

int main()
    cin>>target;
    dfs(0);
    cout<<cnt;
    return 0;

1208. 翻硬币

小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:**oo***oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入格式

两行等长的字符串,分别表示初始状态和要达到的目标状态。

输出格式

一个整数,表示最小操作步数

数据范围

输入字符串的长度均不超过100。
数据保证答案一定有解。

输入样例1:

**********
o****o****

输出样例1:

5

输入样例2:

*o**o***o***
*o***o**o***

输出样例2:

1
#include<bits/stdc++.h>//从头搜索,翻转不一样位置的后面的硬币不能对此位置造成影响,翻前一个则会打乱前面相同的字符,产生新的不同字符,所以最优就是翻此硬币(贪心)
using namespace std;
int main()
    string start,target;
    int step=0;
    cin>>start>>target;
    int n=start.size();
    for(int i=0;i<n-1;i++)
        if(start[i]!=target[i])
            if(start[i]=='*') start[i]='o';
            else start[i]='*';
            if(start[i+1]=='*') start[i+1]='o';
            else start[i+1]='*';
            step++;
        
    
    cout<<step;
    return 0;

116. 飞行员兄弟

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 16 个把手的冰箱。

已知每个把手可以处于以下两种状态之一:打开或关闭。

只有当所有把手都打开时,冰箱才会打开。

把手可以表示为一个 4×4的矩阵,您可以改变任何一个位置 [i,j][i,j] 上把手的状态。

但是,这也会使得第 i 行和第 jj列上的所有把手的状态也随着改变。

请你求出打开冰箱所需的切换把手的次数最小值是多少。

输入格式

输入一共包含四行,每行包含四个把手的初始状态。

符号 + 表示把手处于闭合状态,而符号 - 表示把手处于打开状态。

至少一个手柄的初始状态是关闭的。

输出格式

第一行输出一个整数 N,表示所需的最小切换把手次数。

接下来 N 行描述切换顺序,每行输出两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。

数据范围

1≤i,j≤4

输入样例:

-+--
----
----
-+--

输出样例:

6
1 1
1 3
1 4
4 1
4 3
4 4
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> PII;
const int N = 4, INF = 100;

int change[N][N];

int get(int x, int y)

    return x * 4 + y;




int main()
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
        
            for (int k = 0; k < N; k ++ ) change[i][j] += (1 << get(i, k)) + (1 << get(k, j));
            change[i][j] -= 1 << get(i, j);
        
    int state = 0;
    for (int i = 0; i < N; i ++ )
    
        string line;
        cin >> line;
        for (int j = 0; j < N; j ++ )
            if (line[j] == '+')
                state += 1 << get(i, j);
    
    vector<PII> path;
    for (int i = 0; i < 1 << 16; i ++ )//枚举按下的所有情况(位置一样即可,先后顺序不影响)
    
        int now = state;
        vector<PII> temp;
        for (int j = 0; j < 16; j ++ )
            if (i >> j & 1)
            
                int x = j / 4, y = j % 4;
                now ^= change[x][y];
                temp.push_back(x, y);
            
        if (!now && (path.empty() || path.size() > temp.size())) path = temp;
    

    cout << path.size() << endl;
    for (auto &p : path)
        cout << p.first + 1 << ' ' << p.second + 1 << endl;

    return 0;

第二讲 二分与前缀和

789. 数的范围

给定一个按照升序排列的长度为 n的整数数组,以及 q个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

输入格式

第一行包含整数 n 和 q,表示数组长度和询问个数。

第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。

接下来 q 行,每行包含一个整数 k,表示一个询问元素。

输出格式

共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回 -1 -1

数据范围

1≤n≤100000
1≤q≤10000
1≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1
难度:简单
时/空限制:1s / 64MB
总通过数:32991
总尝试数:54013
来源:模板题,AcWing
算法标签二分
#include<iostream>
using namespace std;

const int N=100010;
int q[N];
int main()
    int n,c,x;
    cin>>n>>c;
    for(int i=0;i<n;i++) cin>>q[i];
    while(c--)
        cin>>x;
        int l=0,r=n-1;
        while(l<r)
            int mid=l+r>>1;
            if(q[mid]<x) l=mid+1;
            else r=mid;   
        
    if(q[l]!=x)
        cout<<"-1 -1"<<endl;
        continue;
    
    int l1 = l, r1 = n-1;
    while(l1<r1)
        int mid=l1+r1+1>>1;
        if(q[mid]<=x) l1=mid;
        else r1=mid-1;   
    
    cout<<l<<" "<<l1<<endl;
    
    return 0;

整数二分算法模板 —— 模板题 AcWing 789. 数的范围
bool check(int x) /* ... */ // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)

    while (l < r)
    
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    
    return l;

// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)

    while (l < r)
    
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    
    return l;

浮点数二分算法模板 —— 模板题 AcWing 790. 数的三次方根
bool check(double x) /* ... */ // 检查x是否满足某种性质

double bsearch_3(double l, double r)

    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    
    return l;

790. 数的三次方根

给定一个浮点数 n,求它的三次方根。

输入格式

共一行,包含一个浮点数 n。

输出格式

共一行,包含一个浮点数,表示问题的解。

注意,结果保留 6 位小数。

数据范围

−10000≤n≤10000

输入样例:

1000.00

输出样例:

10.000000
难度:简单
时/空限制:1s / 64MB
总通过数:23870
总尝试数:45712
来源:模板题,AcWing
算法标签二分
#include<iostream>
#include<iomanip>
using namespace std;
double l,r,mid,n;
double thr(double n)
	return n*n*n;

int main()
	cin>>n;
	l=-25, r=25;
	while(r-l>1e-7)
		mid=(l+r)/2;
		if(thr(mid)>=n) r=mid;
		else l=mid;
	
	cout<<fixed<<setprecision(6)<<l;
	return 0;
 

730. 机器人跳跃问题

机器人正在玩一个古老的基于 DOS 的游戏。

游戏中有 N+1座建筑——从 0 到 N 编号,从左到右排列。

编号为 0 的建筑高度为 0 个单位,编号为 i 的建筑高度为 H(i) 个单位。

起初,机器人在编号为 0 的建筑处。

每一步,它跳到下一个(右边)建筑。

假设机器人在第 k 个建筑,且它现在的能量值是 E,下一步它将跳到第k+1 个建筑。

如果 H(k+1)>E,那么机器人就失去 H(k+1)−E的能量值,否则它将得到 E−H(k+1) 的能量值。

游戏目标是到达第 N 个建筑,在这个过程中能量值不能为负数个单位。

现在的问题是机器人至少以多少能量值开始游戏,才可以保证成功完成游戏?

输入格式

第一行输入整数 N。

第二行是 N 个空格分隔的整数,H(1),H(2),…,H(N) 代表建筑物的高度。

输出格式

输出一个整数,表示所需的最少单位的初始能量值上取整后的结果。

数据范围

1≤N,H(i)≤10^5

输入样例1:

5
3 4 3 2 4

输出样例1:

4

输入样例2:

3
4 4 4

输出样例2:

4

输入样例3:

3
1 6 4

输出样例3:

3
难度:中等
时/空限制:3s / 64MB
总通过数:4344
总尝试数:9691
来源:今日头条2019,笔试题
算法标签二分递推
#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n;
int h[N];

bool check(int mid)
    for(int i=1;i<=n;i++)
        mid+=mid-h[i];
        if (mid>= 1e5) return true; //防止int溢出
        if(mid<0) return false;
    
    return true;


int main()
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>h[i];
    
    int l=0,r=1e5;
    while(l<r)
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    
    
    cout<<l;
    return 0;

1221. 四平方和

四平方和定理,又称为拉格朗日定理:

每个正整数都可以表示为至多 4 个正整数的平方和。

如果把 00 包括进去,就正好可以表示为 4 个数的平方和。

比如:

5=02+02+12+22
7=12+12+12+22

对于一个给定的正整数,可能存在多种平方和的表示法。

要求你对 4 个数排序:

0≤a≤b≤c≤d

并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法。

输入格式

输入一个正整数 N。

输出格式

输出4个非负整数,按从小到大排序,中间用空格分开。

数据范围

0<N<5∗1060<N<5∗106

输入样例:

5

输出样例:

0 0 1 2
难度:简单
时/空限制:1s / 64MB
总通过数:3882
总尝试数:14618
来源:第七届蓝桥杯省赛C++A/B组,第七届蓝桥杯省赛JAVAB/C组
算法标签二分 哈希
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 2500010;

struct Sum

    int s, c, d;
    bool operator< (const Sum &t)const  //重定义后可使用sort
    
        if (s != t.s) return s < t.s;
        if (c != t.c) return c < t.c;
        return d < t.d;
    
sum[N];

int n, m;

int main()

    cin >> n;

    for (int c = 0; c * c <= n; c ++ )
        for (int d = c; c * c + d * d <= n; d ++ )
            sum[m ++ ] = c * c + d * d, c, d;

    sort(sum, sum + m);

    for (int a = 0; a * a <= n; a ++ )
        for (int b = 0; a * a + b * b <= n; b ++ )
        
            int t = n - a * a - b * b;
            int l = 0, r = m - 1;
            while (l < r)
            
                int mid = l + r >> 1;
                if (sum[mid].s >= t) r = mid;
                else l = mid + 1;
            
            if (sum[l].s == t)
            
                printf("%d %d %d %d\\n", a, b, sum[l].c, sum[l].d);
                return 0;
            
        

    return 0;


1227. 分巧克力

儿童节那天有 K 位小朋友到小明家做客。

小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 N 块巧克力,其中第 ii 块是 Hi×Wi的方格组成的长方形。

为了公平起见,小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。

切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同

例如一块 6×5的巧克力可以切出 6 块 2×2的巧克力或者 2 块 3×3的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入格式

第一行包含两个整数 N 和 K。

以下 N 行每行包含两个整数 Hi和 Wi。

输入保证每位小朋友至少能获得一块 1×1 的巧克力。

输出格式

输出切出的正方形巧克力最大可能的边长。

数据范围

1≤N,K≤10^5
1≤Hi,Wi≤10^5

输入样例:

2 10
6 5
5 6

输出样例:

2
难度:简单
时/空限制:1s / 64MB
总通过数:7643
总尝试数:15754
来源:第八届蓝桥杯省赛C++A/B组,第八届蓝桥杯省赛JAVAA/B组
算法标签二分
#include<bits/stdc++.h>
using namespace std;
const int N=100010;


int h[N],w[N];

int n,k;

bool check(int mid)
    int res=0;
    for(int i=0;i<n;i++)
        res+=(h[i]/mid)*(w[i]/mid);
    
    if(res>=k) return true;
    else return false;


int main()
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=0;i<n;i++)
        cin>>h[i]>>w[i];
    
    int l=0,r=1e5;
    while(l<r)
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    
    cout<<l;
    return 0;

99. 激光炸弹

地图上有 N 个目标,用整数 Xi,Yi表示目标在地图上的位置,每个目标都有一个价值 Wi。

注意:不同目标可能在同一位置。

现在有一种新型的激光炸弹,可以摧毁一个包含 R×R 个位置的正方形内的所有目标。

激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,y 轴平行。

求一颗炸弹最多能炸掉地图上总价值为多少的目标。

输入格式

第一行输入正整数 N 和 R,分别代表地图上的目标数目和正方形的边长,数据用空格隔开。

接下来 N 行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi,分别代表目标的 x 坐标,y 坐标和价值,数据用空格隔开。

输出格式

输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。

数据范围

0≤R≤10^9
0<N≤10000
0≤Xi,Yi≤5000
0≤Wi≤1000

输入样例:

2 1
0 0 1
1 1 1

输出样例:

1
难度:简单
时/空限制:1s / 168MB
总通过数:8583
总尝试数:25487
来源:《算法竞赛进阶指南》, HNOI2003
算法标签二维前缀和
#include <iostream>
using namespace std;

int g[5010][5010];//价值
int N,R;

int main()

    cin>>N>>R;
    int n=R,m=R;//长和宽
    for(int i=0,x,y,w;i<N;i++)
    
        cin>>x>>y>>w;
        x++;
        y++;
        //为了方便处理边界,把前缀和定义从1开始,所以开头要++
        n=max(n,x);
        m=max(m,y);
        //判断长和宽的边界
        g[x][y]+=w;//将他们的价值放进数组里
    
    //输入&预处理
    n = 5001, m = 5001, R = min(R, 5001);
    for(int i=1;i<=n;i++)
    
        for(int j=1;j<=m;j++)
        
            g[i][j]+=g[i-1][j] + g[i][j-1] -g[i-1][j-1];
            //g[i-1][j]=sum1,g[i][j-1]=sum2,g[i-1][j-1]=sum3
        
    
    //求二维前缀和

    int ans=0;
    for(int i=R;i<=n;i++)//长
    
        for(int j=R;j<=m;j++)//宽
        
            ans=max(ans,g[i][j] - g[i-R][j] - g[i][j-R] +g[i-R][j-R]);
        
    
    //求最大值

    cout<<ans<<endl;

    return 0;


1230. K倍区间

给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j][i,j] 是 K 倍区间。

你能求出数列中总共有多少个 K 倍区间吗?

输入格式

第一行包含两个整数 N 和 K。

以下 N 行每行包含一个整数 Ai。

输出格式

输出一个整数,代表 K 倍区间的数目。

数据范围

1≤N,K≤100000
1≤Ai≤100000

输入样例:

5 2
1
2
3
4
5

输出样例:

6
难度:中等
时/空限制:1s / 64MB
总通过数:4754
总尝试数:12418
来源:第八届蓝桥杯省赛C++B组,第八届蓝桥杯省赛JAVAB组
算法标签前缀和
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n, k;
LL s[N], cnt[N];

int main()

    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i ++ )
    
        scanf("%lld", &s[i]);
        s[i] += s[i - 1];
    

    LL res = 0;
    cnt[0] = 1;
    for (int i = 1; i <= n; i ++ )
    
        res += cnt[s[i] % k];
        cnt[s[i] % k] ++ ;
    

    printf("%lld\\n", res);

    return 0;

第三讲 数学与简单DP

1205. 买不到的数目

小明开了一家糖果店。

他别出心裁:把水果糖包成4颗一包和7颗一包的两种。

糖果不能拆包卖。

小朋友来买糖的时候,他就用这两种包装来组合。

当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。

你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。

大于17的任何数字都可以用4和7组合出来。

本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。

输入格式

两个正整数 n,m表示每种包装中糖的颗数。

输出格式

一个正整数,表示最大不能买到的糖数。

数据范围

2≤n,m≤1000
保证数据一定有解。

输入样例:

4 7

输出样例:

17
难度:简单
时/空限制:1s / 64MB
总通过数:3821
总尝试数:6647
来源:第四届蓝桥杯省赛C++A组,第四届蓝桥杯省赛JAVAC组
算法标签数论结论题
//数论题,可打表找规律
#include <iostream>

using namespace std;

int main()

    int p, q;
    cin >> p >> q;
    cout << (p - 1) * (q - 1) - 1 << endl;

    return 0;

1211. 蚂蚁感冒

长 100厘米的细长直杆子上有 n 只蚂蚁。

它们的头有的朝左,有的朝右。

每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有 1 只蚂蚁感冒了。

并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

输入格式

第一行输入一个整数 n, 表示蚂蚁的总数。

接着的一行是 n 个用空格分开的整数 XiXi, 的绝对值表示蚂蚁离开杆子左边端点的距离。

正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。

其中,第一个数据代表的蚂蚁感冒了。

输出格式

输出1个整数,表示最后感冒蚂蚁的数目。

数据范围

1<n<50
0<|Xi|<100

输入样例1:

3
5 -2 8

输出样例1:

1

输入样例2:

5
-10 8 -20 12 25

输出样例2:

3
难度:简单
时/空限制:1s / 64MB
总通过数:2799
总尝试数:5471
来源:第五届蓝桥杯省赛C++A/B组
算法标签数学
//由于碰面后速度都不变,可以看作感染后不掉头,还是按原来的方向走
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
int x[N];
int main()
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>x[i];
    
    int res=1;
    int flag=0;
    int right=0;
    if(x[0]>0)
        for(int i=1;i<n;i++)
            if(x[i]<0&&abs(x[i])>x[0])
                res++;
                flag=1;
            
            if(x[i]>0&&x[i]<x[0]) right++;
        
        if(flag) res+=right;
    else
        for(int i=1;i<n;i++)
            if(x[i]>0&&x[i]<abs(x[0]))
                res++;
                flag=1;
            
            if(x[i]<0&&x[i]<x[0]) right++;
        
        if(flag) res+=right;
    
    cout<<res;
    return 0;

1216. 饮料换购

乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去(但不允许暂借或赊账)。

请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的 n 瓶饮料,最后他一共能喝到多少瓶饮料。

输入格式

输入一个整数 n,表示初始买入的饮料数量。

输出格式

输出一个整数,表示一共能够喝到的饮料数量。

数据范围

0<n<10000

输入样例:

100

输出样例:

149
难度:简单
时/空限制:1s / 64MB
总通过数:2875
总尝试数:4171
来源:第六届蓝桥杯省赛C++A/C组,第六届蓝桥杯省赛JAVAB组
算法标签数学
#include <iostream>

using namespace std;

int main()

    int n;
    cin >> n;

    int res = n;
    while (n >= 3)
    
        res += n / 3;
        n = n / 3 + n % 3;
    

    cout << res << endl;

    return 0;

2. 01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。

接下来有 NN 行,每行两个整数 vi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8
难度:简单
时/空限制:1s / 64MB
总通过数:59912
总尝试数:101193
来源:背包九讲 , 模板题
算法标签背包问题DP
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int dp[N][N],val[N],r[N];
int n,v;

int main()
    cin>>n>>v;
    for(int i=1;i<=n;i++)
        cin>>r[i]>>val[i];
    
    for(int i=1;i<=n;i++)
        for(int w=1;w<=v;w++)
            if(w-r[i]<0) dp[i][w]=dp[i-1][w];
            else
                dp[i][w]=max(dp[i-1][w],dp[i-1][w-r[i]]+val[i]);
            
        
    
    cout<<dp[n][v];
    return 0;

1015. 摘花生

Hello Kitty想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。

输入格式

第一行是一个整数T,代表一共有多少组数据。

接下来是T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。

每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。

输出格式

对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

数据范围

1≤T≤100
1≤R,C≤100
0≤M≤1000

输入样例:

2
2 2
1 1
3 4
2 3
2 3 4
1 6 5

输出样例:

8
16
难度:简单
时/空限制:1s / 64MB
总通过数:9825
总尝试数:12373
来源:《信息学奥赛一本通》
算法标签DP线性DP
#include<bits/stdc++.h>
using namespace std;
const int N=110;

int s[N][N],dp[N][N];


int main()
    int t;
    cin>>t;
    while(t--)
        int m,n;
        cin>>m>>n;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                cin>>s[i][j];
            
    
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=max(dp[i-1][j],dp[i][j-1])+s[i][j];
        
        cout<<dp[m][n]<<endl;
    
    
    return 0;

895. 最长上升子序列

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000
−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4
难度:简单
时/空限制:1s / 64MB
总通过数:16296
总尝试数:24724
来源:模板题,AcWing
算法标签动态规划线性DP最长上升子序列
#include<bits/stdc++.h>
using namespace std;

const int N=1010;
int dp[N],s[N];
int n;

int main()
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i];
    
    dp[1]=1;
    int res=0;
    for(int i=2;i<=n;i++)
        dp[i]=1;
        for(int j=1;j<i;j++)
            if(s[j]<s[i]) dp[i]=max(dp[i],dp[j]+1);
            res=max(res,dp[i]);
        
    
    cout<<res;
    return 0;

1212. 地宫取宝

X 国王有一个地宫宝库,是 n×m 个格子的矩阵,每个格子放一件宝贝,每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。

输入格式

第一行 3 个整数,n,m,k,含义见题目描述。

接下来 n 行,每行有 m 个整数 Ci 用来描述宝库矩阵每个格子的宝贝价值。

输出格式

输出一个整数,表示正好取 k 个宝贝的行动方案数。

该数字可能很大,输出它对 1000000007取模的结果。

数据范围

1≤n,m≤50
1≤k≤12
0≤Ci≤12

输入样例1:

2 2 2
1 2
2 1

以上是关于acwing蓝桥杯的主要内容,如果未能解决你的问题,请参考以下文章

AcWing 蓝桥杯C++ AB组辅导课 第一讲递归与递推

蓝桥杯AcWing 题目题解 - 二分与前缀和差分

AcWing刷题蓝桥杯专题突破-广度优先搜索-bfs(11)

蓝桥杯集训·每日一题AcWing 3728. 城市通电

acwing蓝桥杯

蓝桥杯c语言b站___哪些课程