九进制转十进制
题目链接P2031 - [蓝桥杯2022初赛] 九进制转十进制 - New Online Judge (ecustacm.cn)
思路:简单的进制转换问题,自己算出来答案1487输出即可
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
cout << "1478" << endl;
return 0;
}
顺子日期
题目链接P2032 - [蓝桥杯2022初赛] 顺子日期 - New Online Judge (ecustacm.cn)
思路:枚举。因为在2022年中的顺子日期十分有限,分别将其枚举出来。为20221123 20221012 20221231 20221230 20220120-20220129总共十四个,直接输出答案
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
cout << "14" << endl;
return 0;
}
/**************************************************************
Problem: 2032
User: 2317998220 [Angus]
Language: C++
Result: 正确
Time:1 ms
Memory:1428 kb
****************************************************************/
刷题统计
题目链接P2033 - [蓝桥杯2022初赛] 刷题统计 - New Online Judge (ecustacm.cn)
思路:计算出一周总共能刷多少题,用题目总数整除一周能刷的题目,取余得出剩余的题目。对剩余的题目进行判断即可。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
typedef long long ll;
ll a, b, n, ans;
int main()
{
cin >> a >> b >> n;
ans = n / (5 * a + 2 * b);
ans *= 7;
ll res = n % (5 * a + 2 * b);
if (res < 5 * a)
{
if (res % a == 0)
{
ans += (res / a);
}
else
{
ans += (res / a) + 1;
}
}
else
{
ans += 5;
res -= 5 * a;
if (res % b == 0)ans += (res / b);
else ans += (res / b) + 1;
}
cout << ans << endl;
return 0;
}
修建灌木
题目链接P2034 - [蓝桥杯2022初赛] 修剪灌木 - New Online Judge (ecustacm.cn)
思路:
![](https://img-blog.csdnimg.cn/img_convert/93ea03793692781d380600634eca7b1a.png)
假设我们要求红色的树最高能生长多高,只有两种情况,从又左向右剪再折返,由右向左剪再折返。在这两种情况能生长的最大高度去一个max即可
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
typedef long long ll;
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int ans = max((n - i) * 2, (i-1)*2);
cout << ans << endl;
}
return 0;
}
X进制减法
题目链接P2035 - [蓝桥杯2022初赛] X进制减法 - New Online Judge (ecustacm.cn)
思路:由题目意思可以知道X进制转化为10进制的方法使每个数位的数乘上前几个数位的进制的累乘。
如果两个数相减,如:(A1-B1)x1+(A2-B2)x2+(A3-B3)x3+……我们可以知道Ai-Bi是固定的,所以只需要让xi尽可能小即可,但每个数位的进制肯定比这个数位上的数字大,所以我们只要取
max(2,max(Ai,Bi)+1)即可。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 100010;
const int M = 1e9 + 7;
int a[N], b[N], c[N];
int n, m1,m2;
int main()
{
cin >> n;
cin >> m1;
for (int i = m1; i >0; i--)
{
cin >> a[i];
}
cin >> m2;
for (int i = m2; i >0; i--)
{
cin >> b[i];
}
int len1 = m1;
int len2 = m2;
for (int i = len1; i>0; i--)
{
c[i] = max(2, max(a[i], b[i])+1);
}
ll ansA = 0, ansB = 0;
for (int i = len1; i >0; i--)
{
ansA = (ansA * c[i] + a[i]) % M;
}
for (int i = len2; i >0; i--)
{
ansB = (ansB * c[i] +b[i]) % M;
}
ll ans = (ansA - ansB + M) % M;
cout << ans << endl;
return 0;
}
统计子矩阵
题目链接P2036 - [蓝桥杯2022初赛] 统计子矩阵 - New Online Judge (ecustacm.cn)
思路:二维数组前缀和+双指针。
首先前缀和肯定是必不可少的,如果暴力四层for循环的话时间复杂度就到了O(n^4)很显然会超时。
我们可以换一个思路来想,x1和x2我们依旧用循环。但是我们可以注意到如果出现了一个矩阵内的和如果大于所给条件k,因为矩阵内不存在负数,所以想要继续满足条件,则y1必定向右移动,这和y2的移动方向是一样的,所以我们可以用双指针来写。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdio.h>
using namespace std;
const int N = 510;
typedef long long ll;
ll a[N][N], s[N][N],ans;
int n, m,k;
ll qianzhui(int x1, int y1, int x2, int y2)
{
ll res =s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
return res;
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%ld",&a[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] + a[i][j];
}
}
for(int i=1;i<=n;i++)
for (int j = i; j <= n; j++)
{
for (int l = 1, r = 1; r <= m; r++)
{
while (l <= r && qianzhui(i, l, j, r) > k)l++;
ans += r - l + 1;
}
}
printf("%ld", ans);
return 0;
}
积木画
题目链接P2037 - [蓝桥杯2022初赛] 积木画 - New Online Judge (ecustacm.cn)
思路:动态规划。dp[i]中的i为第几列。
我们首先观察dp[i]可以由哪几个状态转移过来。
![](https://img-blog.csdnimg.cn/img_convert/da16acb059d4aa4f46e5ed4c1401a962.png)
dp[i-1]是一种状态且只有一种方法。
![](https://img-blog.csdnimg.cn/img_convert/f08d555d69b9e83db247798d17e3ad3d.png)
dp[i-2]是一种状态且只有一种方法。
![](https://img-blog.csdnimg.cn/img_convert/d1202085d72f40aa4a383fcf3e7313af.png)
dp[i-3]是一种状态且有两种方法。
![](https://img-blog.csdnimg.cn/img_convert/b2ba18b336a226977ef7af0ee91ff2c8.png)
dp[i-4]是一种状态且有两种方法。
经过画图可以得知dp[i-5]也是一种状态且有两种方法。我们不妨推测
dp[i]=dp[i-1]+dp[i-2]+2*dp[i-3]+2*dp[i-4]+……+2*dp[1],则
dp[i-1]=dp[i-2]+dp[i-3]+2*dp[i-4]+……+2*dp[1];
两式想减即可得到递推式:dp[i]=2*dp[i-1]+dp[i-3].
接下来只要初始化一下dp[1],dp[2],dp[3]即可。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int N = 10000010;
const ll M = 1000000007;
int dp[N];
int n;
int main()
{
cin >> n;
dp[1] = 1;
dp[2] = 2;
dp[3] = 5;
for (int i = 4; i <= n; i++)
{
dp[i] = ((dp[i - 1] * 2)%M + dp[i - 3]%M)%M;
}
cout << dp[n] << endl;
return 0;
}
扫雷
题目链接P2038 - [蓝桥杯2022初赛] 扫雷 - New Online Judge (ecustacm.cn)
思路:dfs+二分
首先扫雷的操作很符合dfs的应用场景,如果不用二分确定能扫到的雷的范围的话,时间复杂度会达到O(n^2)会超时。我们想一下如果地雷一(x1,y1,r1)爆炸,地雷二(x2,y2,r2) |x1-x2|>r1则必不可能被引爆,所以可引爆的地雷的范围是x2-r<=x1<=x2+r,我们先按照x的大小对一个结构体进行排序,然后dfs,再用二分查找两个边界值,即可稍稍的优化一下。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int N = 500010;
int n, m,ans;
double dis(double x1, double y1, double x2, double y2)//判断地雷是不是在爆炸范围内
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
struct dilei
{
int x, y, r;
int used = 0;//标记地雷是不是已经炸过
}a[N];
struct
{
int x, y, r;
}b[N];
bool cmp(dilei a, dilei b)
{
if (a.x < b.x)return true;
else return false;
}
void dfs(int x,int y,int r)
{
int mid1 = 0;
int l = 1, rr = n;
while (l < rr)
{
mid1 = (l + rr) >>1;
if (a[mid1].x >= x-r)rr = mid1;
else l = mid1 + 1;
}
mid1 = l;
int mid2 = 0;
l = 1, rr = n;
while (l < rr)
{
mid2 = (l + rr+1) >> 1;
if (a[mid2].x <= x+r)l = mid2;
else rr = mid2 - 1;
}
mid2 = l;
for (int i = mid1; i <= mid2; i++)
{
if (!a[i].used && dis(x, y, a[i].x, a[i].y) <= r)
{
a[i].used = 1, ans++;
dfs(a[i].x, a[i].y, a[i].r);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%lld %lld %lld", &a[i].x, &a[i].y, &a[i].r);
for (int i = 1; i <= m; i++)scanf("%lld %lld %lld", &b[i].x, &b[i].y, &b[i].r);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= m; i++)dfs(b[i].x, b[i].y, b[i].r);
printf("%d", ans);
return 0;
}
李白打酒加强版
题目链接P2039 - [蓝桥杯2022初赛] 李白打酒加强版 - New Online Judge (ecustacm.cn)
思路:qaq呜呜呜第一次用dfs结果超时了这里是代码。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 210;
int path[N];
typedef long long ll;
int t, n, m;
ll ans, jiu;
void dfs(int x,int a,int b)
{
if (jiu < 0)return;
if (a< 0 || b < 0)return;
if (x == n + m)
{
if (jiu == 1)ans++;
return;
}
for (int i = 0; i < 2; i++)
{
path[x] = i;
if (i == 0)
{
jiu--;
b--;
}
if (i == 1)
{
jiu *= 2;
a--;
}
dfs(x + 1,a,b);
if (i == 0)
{
jiu++;
b++;
}
if (i == 1)
{
jiu /= 2;
a++;
}
}
}
int main()
{
scanf("%d",&t);
while (t--)
{
ans = 0, jiu = 2;
scanf("%d %d",&n,&m);
dfs(1,n, m - 1);
printf("%lld", ans % 1000000007);
}
return 0;
}
嗯然后查了一下要用动态规划写。定义一个数组dp[i][j][k],i是遇到了几家店,j是遇到了几次花,k是还有多少酒。
我们可以知道转移方程:
dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k/2](i&&k%2==0)
dp[i][j][k]=dp[i][j][k]+dp[i][j-1][k+1]
最后输出dp[i][j-1][1]即可。
但是为什么不输出dp[i][j][0]呢?
因为题目中提到,最后一次肯定是遇到了花,但是dp[i][j][0]=dp[i][j-1][1]+dp[i-1][j][0]。很明显这一项dp[i-1][j][0]是指最后一次遇到了店,所以不正确。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 110;
int dp[N][N][N];
int t,n,m;
int main()
{
cin >> t;
while (t--)
{
memset(dp, 0, sizeof(dp));
cin >> n >> m;
dp[0][0][2] = 1;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
for (int k = 0; k <= m; k++)
{
if (i > 0 && k % 2 == 0)dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k / 2])%mod;
if (j > 0)dp[i][j][k] = (dp[i][j][k] + dp[i][j - 1][k + 1])%mod;
}
cout << dp[n][m-1][1]%mod << endl;
}
return 0;
}
砍竹子
题目链接P2040 - [蓝桥杯2022初赛] 砍竹子 - New Online Judge (ecustacm.cn)
思路:首先我们可以知道先砍最长的那一类竹子肯定是最优的选择。我们可以定义一个结构体,里面存柱子的长度和编号,在内部进行运算符重载按照高度降序排列,如果高度相同,则按编号升序排列(代码中会讲为什么要这样做)。再将结构体放入一个优先队列中即可。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
int n,b,ans;
ll a;
struct node
{
ll h ;
int num;
bool operator<(const node& x) const
{
if (h == x.h)return num > x.num;
else return h < x.h;
}
};
priority_queue<node> hp;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
ll x = 0;
scanf("%lld", &x);
hp.push({ x, i });
}
while (hp.top().h != 1)
{
a = hp.top().h;//取队头元素高度
b = hp.top().num-1;//取队头元素编号,-1是为了让队头元素的编号也符合下面的规则
while (hp.top().h == a && hp.top().num == b+1)
{
b++;//因为魔法只能砍相邻的竹子,又由于已经对高度相同的竹子按照编号进行了升序排列,上面的hp.top().num == b+1加上b++的操作可以保证高度相同的相邻竹子被一次砍完
hp.pop();//弹出队头元素
hp.push({ (ll)sqrt(a / 2 + 1), b });//新元素入队
}
ans++;
}
printf("%d", ans);
return 0;
}