2024.4.14第二次阶段性测试

博客给出多道算法题题解。包括简单判断、糖果分配、字符串修改、矩阵操作等题目。部分题可直接计算或暴力枚举,还有多道题涉及二分算法,如糖果堆数、汉堡数量等问题,同时介绍了矩阵旋转、遍历等操作的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总的来说,最近半个月不上课不晚训,很多同学啥也不做不练,二分也没学好,甚至连基本的模拟题都不会写,如果以这种状态继续学下去,那你只能仰望同班同学了。

A题题解
简单判断题,直接输入字符串然后计算即可

#include<bits/stdc++.h>
using namespace std;
char s[10];
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>s+1;
		int pre=0;
		int suf=0;
		for(int i=1;i<=3;i++){
			pre=pre+(s[i]-'0');
		}
		for(int i=4;i<=6;i++){
			suf=suf+(s[i]-'0');
		}
		if(pre==suf){
			cout<<"YES"<<'\n';
		}
		else{
			cout<<"NO"<<'\n';
		}
	}
	return 0;
}

B题题解
每个人都只能分到一样多的并且不允许抓一部分放到别人盒子里面,那么我们只能以所有盒子里面的糖果数量的最小值为基准,所以超过这个最小值的部分吃掉就行了

#include<bits/stdc++.h>
using namespace std;
int A[55];
int main() {
	int t;
	cin>>t;
	while(t--) {
		int n;
		cin>>n;
		int mi=1e9;
		for(int i=1; i<=n; i++) {
			cin>>A[i];
			mi=min(mi,A[i]);
		}
		int ans=0;
		for(int i=1; i<=n; i++) {
			ans=ans+A[i]-mi;
		}
		cout<<ans<<'\n';
	}
	return 0;
}

C题题解
两个字符串修改成一样的,会有一个花费,这个花费说白了就是字母的间距,因为题目不允许成环状修改,所以我们一个字母变成另一个字母的花费就是它们之间的间距。这里其实有个性质,两个字符串 S , T S,T S,T每个字母对 ( S [ i ] , T [ i ] ) (S[i],T[i]) (S[i],T[i]) ,只要是变成这俩字母中间的任意字母,其实花费都是一样的,因此我们直接把 S S S字符串当作最终变化的字符串去计算答案即可,取最小值。

#include<bits/stdc++.h>
using namespace std;
char s[55][10];
int main() {
	int t;
	cin>>t;
	while(t--) {
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			cin>>s[i]+1;
		}
		int mi=1e9;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				int cnt=0;
				for(int k=1;k<=m;k++){
					if(s[i][k]==s[j][k])continue;
					int pos_i=s[i][k]-'a';
					int pos_j=s[j][k]-'a';
					cnt=cnt+abs(pos_i-pos_j);
				} 
				mi=min(mi,cnt);     
			}
		}
		cout<<mi<<'\n';
	}
	return 0;
}

D题题解
其实直接暴力枚举就好了,有同学可能时间复杂度又不会算了
T 组数据 T 至多 1000 T组数据T至多1000 T组数据T至多1000 N , M 至多 200 N,M至多200 N,M至多200,那么一个矩阵的规模也就40000,我们枚举每个点,每个点需要计算一遍左右对角线,也就是至多 ( N + M ) (N+M) (N+M)个点,不过题目也保证了,所有T组数据里面的 N ∗ M N*M NM加起来也不超过4e4…所以完全可以暴力
有时候用while循环是很优雅的写法

#include<bits/stdc++.h>
using namespace std;
int A[205][205];
int main() {
	int t;
	cin>>t;
	while(t--) {
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>A[i][j];
			}
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				int cnt=0;
				int x=i,y=j;
				while(x>=1&&y>=1){
					cnt=cnt+A[x][y];
					x--;
					y--;
				}
				x=i,y=j;
				while(x>=1&&y<=m){
					cnt=cnt+A[x][y];
					x--;
					y++;
				}
				x=i,y=j;
				while(x<=n&&y>=1){
					cnt=cnt+A[x][y];
					x++;
					y--;
				}
				x=i,y=j;
				while(x<=n&&y<=m){
					cnt=cnt+A[x][y];
					x++;
					y++;
				}
				cnt=cnt-3*A[i][j];
				ans=max(ans,cnt);
			}
		}
		cout<<ans<<'\n';
	}
	return 0;
}

E题题解
我没想象到居然这么多同学不会写这个题,甚至还在写Q次询问,for循环
时间复杂度又忘记怎么计算了是吧,1000ms只允许你跑1e8差不多
这个题是个非常明显的二分,问最少吃多少堆糖果就能达到至少X颗
这不就是二分吗,我们把糖果数组排序,从大到小做个前缀和,然后二分找位置就好了。

#include<bits/stdc++.h>
using namespace std;
int A[150005];
int Q[150005];
int sum[150005];
int x;
bool check(int pos){
	return sum[pos]>=x;
}
bool cmpx(int x,int y){
	 return x>y;
}
int main() {
	int t;
	cin>>t;
	while(t--) {
		int n,q;
		cin>>n>>q;
		for(int i=1;i<=n;i++){
			cin>>A[i];
		}
		sort(A+1,A+1+n,cmpx);
		for(int i=1;i<=n;i++){
			sum[i]=sum[i-1]+A[i];
		}
		sum[n+1]=2e9;
		while(q--){
			cin>>x;
			int L=1,R=n+1;
			while(L<R){
				int mid=(L+R)/2;
				if(check(mid))R=mid;
				else L=mid+1;
			}
			if(R==n+1)cout<<-1<<'\n';
			else cout<<R<<'\n';
		}
	}
	return 0;
}

F题题解
也是经典二分答案,直接二分能做多少个汉堡,去检查

#include<bits/stdc++.h>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const int inf = 1 << 30;
const int maxn = 110;

LL need[3], cnt[3], price[3], money;
bool check(LL t){
    LL sum = 0;
    for(int i = 0; i < 3; ++i)
        sum += max(t*need[i]-cnt[i], 0LL) * price[i];
    return money >= sum;
}
int main() {
    string s;
    cin >> s >> cnt[0] >> cnt[1] >> cnt[2] >> price[0] >> price[1] >> price[2] >> money;
    for(int i = 0; i < s.length(); ++i){
        if(s[i] == 'B') ++need[0];
        if(s[i] == 'S') ++need[1];
        if(s[i] == 'C') ++need[2];
    }
    LL mmin = inf, ans = 0;
    for(int i = 0; i < 3; ++i){
        if(need[i] == 0) continue;
        mmin = min(mmin, cnt[i]/need[i]);
    }
    ans += mmin;
    for(int i = 0; i < 3; ++i)
        cnt[i] -= need[i]*mmin;
    LL l = 0, r = money+cnt[0]+cnt[1]+cnt[2];
    while(l < r){
        LL mid = (l+r+1)/2;
        if(check(mid)) l = mid;
        else r = mid-1;
    }
    cout << ans+l << endl;

    return 0;
}

G题题解
二分能直接玩的次数,带回去检验就行了。。。这应该不难吧
注意判断一开始无解的情况,我们最多最多直接充电玩到不能玩,这样子的通关数量还是小于N的话肯定无解。因为电力必须严格大于B才能充电玩,所以计算的时候要注意一点细节

#include<bits/stdc++.h>
using namespace std;
#define int long long 
int k,n,a,b;//没开龙龙
bool check(int x){
	//直接玩x关 
	int cnt=x;//玩的关数 
	int le=k-x*a;//剩余电
	if(le<=0){
		return false;
	}
	if(le>b){	
	cnt=cnt+le/b;
	if(le%b==0)cnt--;
	}
	if(cnt>=n)return true;
	else return false; 
}
signed main(){
	int q;
	scanf("%lld",&q);
	while(q--){
	scanf("%lld%lld%lld%lld",&k,&n,&a,&b);	
	 int now=k/b;
	 if(k%b==0)now--;//整除的话说明全部电力都玩完了,b电力一次,但是我们要预留出“电力严格大于b才能玩”
	 if(now<n){
	 	cout<<-1<<'\n';continue;
	 }
	 int L=0,R=n;
	 while(L<R){
	 int mid=(L+R+1)/2;
	 if(check(mid))L=mid;
	 else R=mid-1;	
	 }	
	 printf("%lld\n",L);
	}
	return 0;
}

后面几个二维矩阵的题目,纯模拟题,多动动手思考,题解后续更新

H题题解
说白了就是经典的一些矩阵操作,简单的模拟一定要会写,不然比赛的时候调试啊各种浪费时间。
第一种操作: 顺时针旋转90度 ,注意我要求大家掌握对于 N 行, M 列 N行,M列 N行,M的矩阵的操作
不难发现,顺时针旋转就是相当于
原本的第一列倒着枚举,从左往右填充到了旋转矩阵的第一行
原本的第二列倒着枚举,从左往右填充到了旋转矩阵的第二行
原本的第三列倒着枚举,从左往右填充到了旋转矩阵的第三行

原本的第 j j j列倒着枚举,从左往右填充到了旋转矩阵的第 j j j
即代码如图所示

	for(int i=1;i<=m;i++){//i表示枚举列 ,原本的第i列旋转后成了新矩阵的第i行 
		for(int j=n,k=1;j>=1;j--,k++){//j表示倒着枚举行 ,从左往右就依次填充到行里面 
			B[i][k]=A[j][i];			
		} 
	}

第二种操作: 逆时针旋转90度 ,注意我要求大家掌握对于 N 行, M 列 N行,M列 N行,M的矩阵的操作
不难发现,逆时针旋转就是相当于
原本的第 M M M列顺着枚举,从左往右填充到了旋转矩阵的第一行
原本的第 M − 1 M-1 M1列顺着枚举,从左往右填充到了旋转矩阵的第二行
原本的第 M − 2 M-2 M2列顺着枚举,从左往右填充到了旋转矩阵的第三行

原本的第 j j j列顺着枚举,从左往右填充到了旋转矩阵的第 M − j + 1 M-j+1 Mj+1
即代码如图所示

for(int i=m,p=1;i>=1;i--,p++){//i表示枚举列,从最后一列开始枚举,从上往下 
		for(int j=1,k=1;j<=n;j++,k++){
			B[p][k]=A[j][i];//固定列,然后枚举行从上往下,依次填入新矩阵			
		} 
	}

第三种操作,中心对称,实际上有一个性质,所有中心对称的点的x方向坐标的和都是定值,y方向上也是的,所以说我们只需要枚举 ( i , j ) (i,j) (i,j) 能直接计算出它跟哪个点对称
本题完整AC代码

#include <bits/stdc++.h>
using namespace std;
char A[12][12];
char B[5][12][12];//B[i][j][k]表示第i种变化后的矩阵的第j行第k列元素 
int main() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>A[i][j];
		}
	}
	bool ok=true;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>B[0][i][j];
			if(A[i][j]!=B[0][i][j])ok=false;
		}
	}
	if(ok==true){
		cout<<4;return 0;//什么也不变 
	}
	for(int i=1;i<=n;i++){
		for(int j=n,k=1;j>=1;j--,k++){
			B[1][i][k]=A[j][i];			
		} 
	}
	for(int i=n,p=1;i>=1;i--,p++){
		for(int j=1,k=1;j<=n;j++,k++){
			B[2][p][k]=A[j][i];			
		} 
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			//中心对称  两个坐标(x ,y)  (a,b) 实际上 x+a   y+b 即 行的和   列的和一定是定值
			//行定值为行数+1    列定值为列数+1			
			int x=n+1-i;
			int y=n+1-j;
			B[3][i][j]=A[x][y];
		} 
	}
	for(int i=1;i<=3;i++){
		bool cnt=true;//假设第i种变化是可以的 
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				if(B[i][j][k]!=B[0][j][k])cnt=false;
			}
		}
		if(cnt==true){
			cout<<i;return 0;
		}
	}
	cout<<5;//程序走到这说明前面四种都不是 
	return 0;
}

其他几种矩阵题目很简单,回型遍历,从上到下遍历,蛇形填充,实际上你只需要用几个变量,控制一下坐标,控制一下走向就可以了。比如说蛇形遍历,就是从最左上角往下拉,拉到走不动的时候马上往走,上面走到顶以后就要往下走了,,,

I题题解
二维数组右上左下遍历

#include <bits/stdc++.h>
using namespace std;
int A[110][110];
int main() {
	int n,m;
	cin>>n>>m;//n行m列 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>A[i][j];
		}
	}
	//策略就是枚举第一行,最后一列的所有点
	//以该点出发直接往下拉,直到走到边界
	for(int i=1;i<=m;i++){
		//枚举第一行的所有起点,依次往左下角枚举
		int x=1,y=i;
		while(x<=n&&y>=1){
			cout<<A[x][y]<<'\n';
			x++;
			y--;
		} 
	}
	for(int i=2;i<=n;i++){
		//枚举最后一列的所有起点,依次往左下角枚举
		//因为有一个点重复 所以直接跳过了  
		int x=i,y=m;
		while(x<=n&&y>=1){
			cout<<A[x][y]<<'\n';
			x++;
			y--;
		} 
	}  
	return 0;
}

神奇的幻方 题解
本题其实就是
普通情况填右上方就行了
但是如果你是第一行,其实是没有右上方这种概念的,那么直接给你调到最后一行去
如果你是最后一列,其实也是没有右上方的,越界了,直接变到第一列去了
当然你们会发现有一个位置很特殊,那么就是(第一行,最后一列)
占据了两种条件,怎么办???
其实它应该最先被判断处理。然后判断第一行,最后一列的情况,然后判断上一个填充的数字的右上方是不是空着的,如果是空着的就放进去,如果不是,那么只能放到下方
我的代码里面 x , y x,y x,y实际上表示的是“上一个数放进去的坐标”

#include <bits/stdc++.h>
using namespace std;
int A[50][50];//初始状态全是0  则表示该位置没有数字 
int main(){
	int n;
	cin>>n;
	n=n*2-1;
	int x=1;
	int y=n/2+1;
	A[x][y]=1;
	for(int i=2;i<=n*n;i++){
		if(x==1&&y==n){
			//最右上角
			x++;
			A[x][y]=i;
			continue; 
		}
		if(x==1){
			x=n;
			y++;
			A[x][y]=i;
			continue;
		}
		if(y==n){
			y=1;
			x--;
			A[x][y]=i;
			continue;
		}
		
		if(A[x-1][y+1]!=0){
			x++;
			A[x][y]=i;
			continue;
		}
		else{
			x--;
			y++;
			A[x][y]=i;
			continue;
		}	
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<A[i][j]<<" ";
		}
		cout<<'\n';
	}
	return 0;
}

数组回型遍历题解
其实同学们你们只需要控制好方向,控制好边界就很好做了

#include <bits/stdc++.h>
using namespace std;
int A[105][105];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>A[i][j];
		}
	}
	//实际上这个题的方向就是
	//右 下 左  上  每个方向都要走到头然后再换方向遍历就行了
	int op=0;//op表示方向
	int vis=0;//表示访问了多少个位置
	int all=n*m;//一共n*m个位置
	int x=1,y=1;//走过的点标记成0  因为题目元素是正的 
	while(vis<all){
		cout<<A[x][y]<<'\n';
		vis++;
		A[x][y]=0;
		if(op==0&&A[x][y+1]==0){
			op=(op+1)%4;
			x++;continue;
		}
		else if(op==1&&A[x+1][y]==0){
			op=(op+1)%4;
			y--;continue;
		}
		else if(op==2&&A[x][y-1]==0){
			op=(op+1)%4;
			x--;continue;
		}
		else if(op==3&&A[x-1][y]==0){
			op=(op+1)%4;
			y++;continue;
		}
		//前面几个if都是判断特定的点  需要拐弯的情况
		if(op==0){
			y++;
		}
		else if(op==1)x++;
		else if(op==2)y--;
		else if(op==3)x--;
	} 
	return 0;
}

蛇形填充矩阵题解

其实也是控制方向,控制需要变向的特殊的点位

#include <bits/stdc++.h>
using namespace std;
int A[105][105];
int main(){
	//很多同学居然打表。。。我表示很无语
	//如果N 是100 你拿什么打表 
	int n;
	cin>>n;
	/*
	本题很好观察,
	填充方式就是从(1,1)开始往右上填充
	到顶以后,从顶部的右边开始左下填充
	左下到底以后从底部的下方继续向右上填充 
	*/ 
	int x=1,y=1;
	int op=0;//0右上方   1左下方填充 
	for(int i=1;i<=n*n;i++){
		A[x][y]=i;
		if(op==0&&(x==1||y==n)){
			//填充到底,但是变化规则不一样
			op=(op+1)%2;
			if(x==1&&y!=n){ 
				y++;
			}
			else if(y==n){
				x++;
			} 
			continue;
		}
		if(op==1&&(x==n||y==1)){
			//左下角填充到底
			op=(op+1)%2;
			if(y==1&&x!=n){
				x++;
			}
			else if(x==n){
				y++;
			}
			continue; 
		}
		//前面两个if是专门判断换向 
		if(op==0){
		  x--;
		  y++;		
		}
		else{
			x++;
			y--;
		}
		//普通if  就按填充方向直接变坐标就行了 
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<A[i][j]<<" ";
		}
		cout<<'\n';
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值