第十三届蓝桥杯大赛软件赛省赛B组C/C++(个人题解)

本文总结了一次编程竞赛中的解题思路和代码实现,涉及九进制转换、日期判断、刷题统计、灌木修剪、进制减法、子矩阵计数、积木拼图、扫雷游戏、李白买酒和竹子砍伐问题。通过实例展示了动态规划、前缀和、双指针等算法的应用,并提供了高效解决方案。
摘要由CSDN通过智能技术生成

思路和代码已更新目前已知的最优解(2022.4.12)


简单思路

试题 A: 九进制转十进制

手算或者程序跑,res = 1478
在这里插入图片描述

试题 B: 顺子日期

手数日历或者写个程序跑一下都行, 我写的是 14。
但是,对于012是不是顺子这件事,我也不晓得呀 ((lll¬ω¬))

20220120
20220121
20220122
20220123
20220124
20220125
20220126
20220127
20220128
20220129

20221012

20221123

20221230
20221231

试题 C: 刷题统计

r e s = n 5 ∗ a + 2 ∗ b res = \frac{n}{5*a+2*b} res=5a+2bn , n = n % ( 5 ∗ a + 2 ∗ b ) n = n \%(5*a+2*b) n=n%(5a+2b)
再看一下剩下的 n 要写几天。

直接循环一天一天的减肯定超时

试题 D: 修剪灌木

对于第 i 课树, r e s = m a x ( i − 1 , n − i ) ∗ 2 res = max(i-1,n-i)*2 res=max(i1,ni)2

如图所示:这里是引用

试题 E: X 进制减法

我愿称它为最恶心的一个题。
如果 A − B > = 0 A - B >= 0 AB>=0, 对于每一位选中的进制为 m a x ( a i + 1 , b i + 1 , 2 ) max(a_i+1,b_i+1,2) max(ai+1,bi+1,2)
如果 A − B < 0 A - B < 0 AB<0, 对于每一位选中的进制为 n n n

学弟赛后给我说题目保证 A 大于等于 B,那么就可以忽略 A - B < 0 这种情况(存下来,下次校赛出)
解释 X 进制数如何转化为 10 进制数: 例如说某种 X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制,则 X 进制数 321 转换为十进制数为 65。
32 1 x = ( 3 ∗ 10 ∗ 2 ∗ 1 + 2 ∗ 2 + 1 ∗ 1 ) 10 = 6 5 10 321_x = (3 * 10 * 2 * 1+ 2 * 2 + 1 * 1)_{10} = 65_{10} 321x=(31021+22+11)10=6510
题目要求, A - B 在 10 进制下最小。结合上述解释得,从低位到高位,每一位的进制尽可能小即可。
在保证合法的基础上,每一位的进制为 m a x ( a i + 1 , b i + 1 , 2 ) max(a_i+1,b_i+1,2) max(ai+1,bi+1,2)
综上,输入中的 n 是没有用的~~

试题 F: 统计子矩阵

我的写法是:二维前缀和+二分枚举,时间复杂度 O ( n 3 ∗ l o g 2 ( n ) ) O(n^3*log_2(n)) O(n3log2(n))

具体写法:
枚举每一个点(i,j)作为矩阵的左上角,再遍历右下角点(x,y) 中的 x,最后二分找合法的最大的 y。

但是,大佬们说,会超时。更优的写法是:双指针,时间复杂度 O ( n 3 ) O(n^3) O(n3)

具体写法:
枚举矩阵的所在行i 和 j (上边界和下边界),双指针分别表示矩阵的两个列 l 和 r。
在这里插入图片描述

试题 G: 积木画

状压DP。

状态定义:
d p i , 1 dp_{i,1} dpi,1 表示前i-1列摆满,第 i 列第一行放了积木的方案数
d p i , 2 dp_{i,2} dpi,2 表示前i-1列摆满,第 i 列第二行放了积木的方案数
d p i , 0 dp_{i,0} dpi,0 表示前i列摆满的方案数

初始化:
d p 0 , 0 = 1 dp_{0,0} = 1 dp0,0=1
d p 1 , 0 = 1 dp_{1,0} = 1 dp1,0=1
d p 2 , 1 = d p 2 , 2 = 1 dp_{2,1} = dp_{2,2} = 1 dp2,1=dp2,2=1

转义状态( i > 1):
d p i + 1 , 1 = d p i , 2 + d p i − 1 , 0 dp_{i+1,1} = dp_{i,2} + dp_{i-1,0} dpi+1,1=dpi,2+dpi1,0
d p i + 1 , 2 = d p i , 1 + d p i − 1 , 0 dp_{i+1,2} = dp_{i,1} + dp_{i-1,0} dpi+1,2=dpi,1+dpi1,0
d p i , 0 = d p i − 1 , 1 + d p i − 1 , 2 + d p i − 1 , 0 + d p i − 2 , 0 dp_{i, 0} = dp_{i-1,1} + dp_{i-1,2} + dp_{i-1, 0} + dp_{i-2,0} dpi,0=dpi1,1+dpi1,2+dpi1,0+dpi2,0

最后 r e s = d p n , 0 res = dp_{n,0} res=dpn,0

画图看一下就没问题了
洛谷原题(亲测我的DP思路没错),题目链接:https://www.luogu.com.cn/problem/P1990
如果感觉内存紧张,可以滚动数组处理一下

试题 H: 扫雷

几何题咱不会呀,大佬们说 leetcode 上有类似的。
写了个暴力代码,每次给出一个手雷就去暴力统计,混百分之四十没问题。

学弟说,这个题目里边 r ≤ 10 r ≤ 10 r10(赛中的时候没有注意到这个地方)。
这个题可以用 O ( n ∗ r 2 ) O(n*r^2) O(nr2) 的时间复杂度建图,如果地雷 i 可以引爆地雷 j,那就让 i 指向 j。
建图操作,对于第 i 个地雷 ( x i , y i ) (x_i,y_i) (xi,yi) b f s bfs bfs 搜索其周围的点 ( x x , y y ) (xx, yy) (xx,yy),两点间距离小于等于 r 且有雷就连线。
对于每一个火箭,通过上述相同的 b f s bfs bfs 操作去引爆地雷即可,时间复杂度 O ( m ∗ r 2 ) O(m*r^2) O(mr2)
题目没有交代,默认为一个地雷只能被引爆一次吧,引爆地雷的操作时间复杂度为 O ( n ) O(n) O(n)
综合时间复杂度 O ( n ∗ r 2 + m ∗ r 2 + n ) O(n*r^2 + m*r^2 + n) O(nr2+mr2+n)

试题 I: 李白打酒加强版

DP。

状态定义:
d p i , j , k dp_{i,j,k} dpi,j,k 表示前 i 个操作中有 j 次喝酒并且酒还有 k 斗的方案数

初始化:
d p 0 , 0 , 2 = 1 dp_{0,0,2} = 1 dp0,0,2=1

转义状态:
k ∗ 2 < = m k*2 <= m k2<=m 时, d p i + 1 , j , k = d p i + 1 , j , k + d p i , j , k dp_{i+1,j,k} = dp_{i+1,j,k} + dp_{i,j,k} dpi+1,j,k=dpi+1,j,k+dpi,j,k
k > 0 k > 0 k>0 时, d p i + 1 , j + 1 , k + 1 = d p i + 1 , j + 1 , k + 1 + d p i , j , k dp_{i+1,j+1,k+1} = dp_{i+1,j+1,k+1} + dp_{i,j,k} dpi+1,j+1,k+1=dpi+1,j+1,k+1+dpi,j,k
ps:注意最后一次操作必须是喝酒

最后 r e s = d p n + m , m , 0 res = dp_{n+m,m,0} res=dpn+m,m,0

试题 J: 砍竹子

优先队列维护最大值,并查集实现区间合并。
分析题目给出的操作可得:每次操作后竹子都会变短,并且竹子变矮的路线是唯一的,故而每次选择最高的竹子操作即可(贪心的想,只有每次砍高的才可能出现更多的等高区间)。

相同高度且相邻的竹子看作一个可操作区间,每次选取高度最高的区间砍,用优先队列维护竹子的高度。

在每次砍竹子以后,竹子的新高度可能导致竹子与左右相邻的竹子高度一致,用并查集维护区间合并(用set什么的都可以)。

每一个区间当作一个结点,如果两个相邻的结点的高度和当前结点高度相同就需要合并,合并为一个新结点。

参考代码

A: 九进制转十进制

#include<bits/stdc++.h>
#include<queue>
#define ll long long
#define IOS ios::sync_with_stdio(false)
using namespace std;

const int maxn = 2e5 + 5;


int main(){
	
	IOS;
	
	int n = 2022;
	int res = 2*9*9*9 + 0*9*9 + 2*9 + 2;// 1478
	cout << res << endl;
	
	while(res){
		cout << res%9;
		res /= 9;
	}
	cout << endl;
	
	
	return 0;
}

B: 顺子日期 (这里012是算顺子的…)

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
using namespace std;

const int maxn = 2e5 + 5;

int a[100] = {0,2,0,2,2};
int day[15] = {0,31,28,31,30,31,30,31,31,30,31,30,31};


int main(){
	
	IOS;
	
	int res = 0;
	for(int i = 1; i <= 12; i++){
		a[5] = i / 10; a[6] = i % 10;
		for(int j = 1; j <= day[i]; j++){
			a[7] = j / 10, a[8] = j % 10;
			for(int k = 3; k <= 8; k++){
				if(a[k-2]+1 == a[k-1] && a[k-1]+1 == a[k]){
					printf("%3d  2022%02d%02d\n", ++res, i, j);
					break;
				}
			}
		}
	}
	cout << res << endl;
	// 14
	
	return 0;
}

C: 刷题统计

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
using namespace std;

const int maxn = 2e5 + 5;

int main(){
	
	IOS;
	
	ll a, b, n;
	cin >> a >> b >> n;
	
	ll res = n / (a*5+b*2) * 7;
	n %= (a*5+b*2);
	
	for(int i = 1; i <= 5; i++) if(n > 0) res++, n -= a;
	for(int i = 1; i <= 2; i++) if(n > 0) res++, n -= b;
	
	cout << res << endl;
	
	return 0;
}

D: 修剪灌木

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;

const int maxn = 2e5 + 5;

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

	
	return 0;
}

E: X 进制减法

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 5;

ll a[maxn], b[maxn], c[maxn];

int main(){
	
	IOS;
	
	int n;
	cin >> n;
	int m1, m2;
	cin >> m1;
	for(int i = 1; i <= m1; i++) cin >> a[m1-i+1];
	cin >> m2;
	for(int i = 1; i <= m2; i++) cin >> b[m2-i+1];
	
	int len = max(m1, m2) + 1;
	for(int i = 1; i < len; i++) c[i] = a[i] - b[i];
	ll res = 0, last = 1;
	for(int i = 1; i < len; i++){
		int tmp = max(a[i], b[i]) + 1;
		tmp = max(tmp, 2);
		if(c[i] < 0){
			c[i] = c[i] + tmp;
			c[i+1]--;
		}
		res = res + c[i] * last % mod;
		res %= mod;
		last = tmp * last % mod;
	}
	
	cout << res << endl;
	
	return 0;
}

/*
11
3
10 4 0
3
1 2 0

8
4
4 3 2 1
4
5 3 2 1
*/

F: 统计子矩阵 (二分写法,大样例可能会超时)

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 5;

ll a[505][505], b[505][505];

ll sum(int i, int j, int x, int y){
	ll res = b[x][y] - b[i-1][y] - b[x][j-1] + b[i-1][j-1];
	return res;
}

int main(){
	
	IOS;

	ll n, m, k;
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> a[i][j];
			b[i][j] = a[i][j] + b[i][j-1] + b[i-1][j] - b[i-1][j-1];
		}
	}
	
	ll res = 0;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			for(int x = i; x <= n; x++){
				int l = j, r = m, y = -1;
				while(l <= r){
					int mid = l + r >> 1;
					if(sum(i, j, x, mid) <= k){
						y = mid;
						l = mid + 1;
					}
					else r = mid - 1;
				}
				if(y != -1) res += y-j+1;
			}
		}
	}
	
	cout << res << endl;
	
	return 0;
}

/*
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
19
*/

F: 统计子矩阵 (双指针写法)

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 5;

ll a[505][505], b[505][505];

ll sum(int i, int j, int x, int y){
	ll res = b[x][y] - b[i-1][y] - b[x][j-1] + b[i-1][j-1];
	return res;
}

int main(){
	
	IOS;
	
	ll n, m, k;
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> a[i][j];
			b[i][j] = a[i][j] + b[i][j-1] + b[i-1][j] - b[i-1][j-1];
		}
	}
	
	ll res = 0;
	for(int i = 1; i <= n; i++){
		for(int j = i; j <= n; j++){
			ll cnt = m * (m+1) / 2, tot = 0, l = 1, r = 0;
			while(r+1 <= m){
				r++;
				tot += sum(i, r, j, r);
				while(tot > k){
					cnt -= (m - r + 1);
					tot -= sum(i, l, j, l);
					l++;
				}
			}
			res += cnt;
		}
	}
	
	cout << res << endl;
	
	return 0;
}

/*
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
19
*/

G: 积木画

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 1e7 + 5;

ll dp[maxn][3];

int main(){
	
	IOS;

	int n;
	cin >> n;
	
	dp[0][0] = 1;
	dp[1][0] = 1;
	dp[2][1] = dp[2][2] = 1;
	
	for(int i = 2; i <= n; i++){
		dp[i+1][1] = (dp[i+1][1] + dp[i-1][0] + dp[i][2]) % mod;
		dp[i+1][2] = (dp[i+1][2] + dp[i-1][0] + dp[i][1]) % mod;
		
		dp[i][0] = (dp[i][0] + dp[i-1][1] + dp[i-1][2] + dp[i-2][0] + dp[i-1][0]) % mod;
	}
	
	cout << dp[n][0] << endl;
	
	return 0;
}

/*
3
5
*/

H: 扫雷 (暴力混分版)

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 5;

typedef struct Node{
	double x, y, r;
	int flag;
} node;

int n, m;
node a[maxn];

bool check(node A, node B){
	double len = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); 
	len = sqrt(len);
	if(B.r > len) return 1;
	return 0;
}

void solve(int id){
	a[id].flag = 0;
	for(int i = 1; i <= n; i++){
		if(a[i].flag == 0) continue;
		if(check(a[i], a[id])){
			solve(i);
		}
	}
}

int main(){
	
	IOS;
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y >> a[i].r, a[i].flag = 1;
	node b;
	for(int i = 1; i <= m; i++){
		cin >> b.x >> b.y >> b.r;
		for(int j = 1; j <= n; j++){
			if(a[i].flag == 0) continue;
			if(check(a[i], b)){
				solve(i);
			}
		}
	}
	
	int res = n;
	for(int i = 1; i <= n; i++) res -= a[i].flag;
	cout << res << endl;
	
	return 0;
}

/*
2 1
2 2 4
4 4 2
0 0 5
2
*/	

I: 李白打酒加强版

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 1e7 + 5;

ll dp[205][105][105];
// res = dp[n+m][m][0]
int main(){
	
	memset(dp, 0, sizeof(0));
	
	IOS;

	int n, m;
	cin >> n >> m;
	dp[0][0][2] = 1;
	for(int i = 0; i <= n+m; i++){
		for(int j = min(i, m); j >= 0; j--){
			for(int k = m; k >= 0; k--){
				if(k*2 <= m && i+1 != n+m) dp[i+1][j][k*2] = (dp[i+1][j][k*2] + dp[i][j][k]) % mod;
				if(k > 0) dp[i+1][j+1][k-1] = (dp[i+1][j+1][k-1] + dp[i][j][k]) % mod;
			}
		}
	}
	
	cout << dp[n+m][m][0] << endl;
	
	return 0;
}
/*
5 10
14
*/

J: 砍竹子

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false)
#define endl "\n"
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 5;

priority_queue<pair<ll, int> > qu;

int f[maxn];
int l[maxn], r[maxn];
int find(int x){
	if(x == f[x]) return x;
	else return f[x] = find(f[x]);
}

void join(int x, int y){
	int fx = find(x);
	int fy = find(y);
	if(fx != fy){
		f[fx] = fy;
		l[fy] = l[fx] = min(l[fy], l[fx]);
		r[fy] = r[fy] = max(r[fy], r[fx]);
	}
}

ll a[maxn];

int main(){
	
	IOS;

	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	
	for(int i = 1; i <= n; i++) f[i] = i;
	for(int i = 1; i <= n; i++) l[i] = i-1;
	for(int i = 1; i <= n; i++) r[i] = i+1;
	
	for(int i = 2; i <= n; i++){
		if(a[i] == a[i-1]){
			join(i, i-1);
		}
	}
	
	for(int i = 1; i <= n; i++) if(f[i] == i && a[i] != 1) qu.push(make_pair(a[i], i));
	
	int res = 0;
	while(!qu.empty()){
		ll id = qu.top().second, h = qu.top().first;
		qu.pop();
		id = find(id);
		if(a[id] != h) continue; 
		res++;
		a[id] = sqrt(h/2+1); 
		if(l[id] != 0 && a[id] == a[find(l[id])]) join(id, l[id]);
		if(r[id] != n+1 && a[id] == a[find(r[id])]) join(id, r[id]);
		if(a[id] != 1) qu.push(make_pair(a[id], find(id)));
	}
	
	cout << res << endl;
	
	return 0;
}

/*
6
2 1 4 2 6 7 
5
*/

总结

题目涉及到的算法多为常见算法,个人感觉难度比不上XCPC省赛。
怀念可以让学弟敲代码,我自己口嗨的日子~~~

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值