总的来说,最近半个月不上课不晚训,很多同学啥也不做不练,二分也没学好,甚至连基本的模拟题都不会写,如果以这种状态继续学下去,那你只能仰望同班同学了。
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
N∗M加起来也不超过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
M−1列顺着枚举,从左往右填充到了旋转矩阵的第二行
原本的第
M
−
2
M-2
M−2列顺着枚举,从左往右填充到了旋转矩阵的第三行
…
原本的第
j
j
j列顺着枚举,从左往右填充到了旋转矩阵的第
M
−
j
+
1
M-j+1
M−j+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;
}