2020-2-15模拟赛题解

前言

这场比赛我发挥欠佳,各种小失误不断,也需要我自己去反思吧,以后打模拟赛也要有选择性地去打,毕竟一天 3 3 场比赛肯定会有影响。

正文

1. 子数整数

错点

考试的时候蒟蒻把 No 打成了 No

分析

这道题实际上就是个小模拟,您只要会 f o r for 循环就可以轻松过掉此题。

答案是直接统计一下就好了。

我们可以直接从 1000030000 循环,算出 s1s2s3

for(int i=10000;i<=30000;i++){
    int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
    if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
template<typename T>void writen(T x){
    write(x);
    puts("");
}
int k,s1,s2,s3,g,s,b,q,w;
bool f;
int main(){
	read(k);
    for(int i=10000;i<=30000;i++){
        int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
        if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
    }if(f==0)puts("No");//考试时这里直接挂掉,打成了NO
    return 0;
}

2. 安全逃离

错点

蒟蒻向老师报告题目有问题后没有认真看题,结果就死了。

分析

做到这道题的时候,我们考到数据范围很小,考虑高复杂度的做法。

我的做法是最暴力的做法,复杂度大概是 O ( c r 2 × + r c 2 × n ) O(cr^2 \times + rc^2 \times n) ,面对这么小的这么小的数据,这个做法显然能过。

我们先写一个 c h e c k check 函数,判断此时的矩阵是否安全。

bool check(){
    for(int i=1;i<=r;++)
        for(int j=1;j<=c;j++)
            if(a[i][j]){//此点有牛
                int s=0;
                for(int l=1;l<=i;l++)
                    if(a[l][j]){
                        s++;
                        break;
                    }
                for(int l=j;l<=n;l++)
                    if(a[i][l]){
                        s++;
                        break;
                    }
                if(s==2)return false;//如果有牛上面和右面都被堵死了,这个矩阵显然是不安全的
            }
    return true;
}

当然还有更快速地写法啦

bool save(){
	for(int i=1;i<=k;i++){
		int flag=0;
		for(int l=x[i]-1;l>=1;l--)
			if(a[l][y[i]]){
				flag++;
				break;
			}
		for(int l=y[i]+1;l<=n;l++)
			if(a[x[i]][l]){
				flag++;
				break;
			}
		if(flag==2)return false;
	}
	return true;
}

我们再写一个函数去枚举移掉哪头牛

void work(){
	int f=1;
	for(int i=1;i<=k;i++){
		a[x[i]][y[i]]--;
		if(save()){//如果安全
			f=0;
			write(i);//输出
			puts("");
		}
		a[x[i]][y[i]]++;
	}if(f)puts("-1");//无解
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
const int MAXN=50+10,MAXK=100+10;
int a[MAXN][MAXN],n,m,k,x[MAXK],y[MAXK];
bool save(){
	for(int i=1;i<=k;i++){
		int flag=0;
		for(int l=x[i]-1;l>=1;l--)
			if(a[l][y[i]]){
				flag++;
				break;
			}
		for(int l=y[i]+1;l<=n;l++)
			if(a[x[i]][l]){
				flag++;
				break;
			}
		if(flag==2)return false;
	}
	return true;
}
void work(){
	int f=1;
	for(int i=1;i<=k;i++){
		a[x[i]][y[i]]--;//移除
		if(save()){
			f=0;
			write(i);
			puts("");
		}
		a[x[i]][y[i]]++;//注意回溯
	}if(f)puts("-1");
}
int main(){
	read(n);read(m);read(k);
	for(int i=1;i<=k;i++){
		read(x[i]);read(y[i]);
		a[x[i]][y[i]]++;
	}
	if(save())puts("0");
	else work();
	return 0;
}

3. 阶乘问题

错点

蒟蒻竟然把 / 10 / 10 打成了 % 10 \% 10

分析

做法显然

由于数字极大,我们应该先对 1 1 ~ n n 的这些数字把末尾的 0 0 全部删掉。

while(x%10==0)x/=10;

再把乘好后的 s s 末尾的 0 0 也都去掉。

关于为什么末尾会有 0 0 ,我来举个例子 2 × 5 = 10 2 \times 5=10 ,所以我们还要再筛一遍 0 0

while(s%10==0)s/=10;

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
int main(){
	int n,s=1;
	read(n);
	for(int i=1;i<=n;i++){
		int x=i;
		while(x%10==0)x/=10;
		s*=x;
		while(s%10==0)s/=10;
	}write(s);
	return 0;
}

4. 拱猪计分

分析

典型的大模拟。

明白了什么叫 1 h 1h 读题, 0.2 h 0.2h 写代码

我们从读入开始

读入

for(int i=1;i<=4;i++){
    read(size[i]);
    for(int j=1;j<=size[i];j++){
        char k;int p;
        cin>>k>>p;
        if(k=='H')card[i][p]=true;
        else if(k=='S')card[i][14]=true;
       		else if(k=='D')card[i][15]=true;
        		else if(k=='C')card[i][16]=true;
    }
}

我们用 c a r d i , j card_{i,j} 记录第 i i 个人,有没第 j j 张得分牌。

计分

for(int i=1;i<=4;i++){
	bool flag=true;
	for(int j=1;j<=13;j++)
		if(card[i][j]==false){
			flag=false;
			break;
		}
	if(flag){//有没有红心牌
		int ans=200;//所有红心牌以+200分计算。
		if(card[i][14]&&card[i][15])ans=500;//若S12、D11皆在吃下所有红心牌之一家,则此玩家得+500分。 
		else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
		if(card[i][16])ans*=2;//若除了C10还有其它计分牌,则将其它计分牌所得分数加倍计算。 
		work(ans);//输出
		putchar(32);//输出
	}else{
		bool flg=true;
		for(int j=1;j<=15;j++)
			if(card[i][j]==true){
				flg=false;
				break;
			}
		if(flg){
			if(card[i][16])work(50);//若持有C10的玩家只有该张牌而没有任何其它牌则得+50分
			else work(0);//若未持有这16张牌之任一张则以得零分计算。 
			putchar(32);//输出
		}else{
			int ans=0;
			for(int j=1;j<=15;j++)
				if(card[i][j])ans+=fraction[j];
			if(card[i][16])ans*=2;
			work(ans);//输出
			putchar(32);//输出
		}
    }
}puts("");

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
void work(int x){
	if(x<0)cout<<x;
	else if(x>0)cout<<'+'<<x;
		else cout<<x;
}
const int fraction[17]={0,-50,-2,-3,-4,-5,-6,-7,-8,-9,-10,-20,-30,-40,-100,+100,0};
int size[5];
bool card[5][17];
int main(){
	while(1){
		memset(card,false,sizeof(card));
		for(int i=1;i<=4;i++){
			read(size[i]);
			for(int j=1;j<=size[i];j++){
				char k;int p;
				cin>>k>>p;
				if(k=='H')card[i][p]=true;
				else if(k=='S')card[i][14]=true;
					else if(k=='D')card[i][15]=true;
						else if(k=='C')card[i][16]=true;
			}
		}
		if(size[1]+size[2]+size[3]+size[4]==0)return 0;
		for(int i=1;i<=4;i++){
			bool flag=true;
			for(int j=1;j<=13;j++)
				if(card[i][j]==false){
					flag=false;
					break;
				}
			if(flag){
				int ans=200;
				if(card[i][14]&&card[i][15])ans=500;
				else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
				if(card[i][16])ans*=2;
				work(ans);
				putchar(32);
			}else{
				bool flg=true;
				for(int j=1;j<=15;j++)
					if(card[i][j]==true){
						flg=false;
						break;
					}
				if(flg){
					if(card[i][16])work(50);
					else work(0);
					putchar(32);
				}else{
					int ans=0;
					for(int j=1;j<=15;j++)
						if(card[i][j])ans+=fraction[j];
					if(card[i][16])ans*=2;
					work(ans);
					putchar(32);
				}
			}
		}puts("");
	}
	return 0;
}

5. 变色龙

错点

b f s bfs p r i o r i t y priority _ q u e u e queue 优化写假了。

分析

最短路问题。

为什么能使用 d i j dij 跑最短路?明显会 T T 啊!

点数: 2000 × 2000 = 400 , 0000 2000 \times 2000=400,0000

边数:小学奥数之等差数列

0 + 4 + + 7996 2000 = ( 7996 + 0 ) × 2000 ÷ 2 = 7996 × 1000 = 799 , 6000 \begin{matrix}\underbrace{0+4+\cdots+7996}\\2000项\end{matrix}=(7996+0) \times 2000 \div 2=7996 \times 1000=799,6000

我们知道 d i j dij 的复杂度是 O ( n log m ) O(n \log m) 简单计算一下 log m = log 7996000 26 \log m=\log 7996000 \approx 26 26 × 4000000 = 1 , 0400 , 0000 > 1 , 0000 , 0000 26 \times 4000000=1,0400,0000 > 1,0000,0000‬

所以显然是会 T T 掉的~~(除非玄学卡常,比如:等式展开之类的)~~

b f s bfs 的期望复杂度是 O ( n + m ) O(n+m) 的。

b f s bfs 大概就是这种写法

q.push((node){xx,yy,0});
	while(q.size()){
	node f=q.top();
	q.pop();
	for(int i=0;i<4;i++){
		int x=f.x+dx[i],y=f.y+dy[i];
		if(x>=1&&x<=n&&y>=1&&y<=m){
			int as=ans[f.x][f.y];
			if(a[x][y]!=a[f.x][f.y])as++;
			if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
		}
	}
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
struct node{
    int x,y;
};
const int MAXN=2000+10;
int n,m,xx,yy,a[MAXN][MAXN],ans[MAXN][MAXN];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
queue<node>q;
int main(){
	read(n);read(m);read(xx);read(yy);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			read(a[i][j]);
	q.push((node){xx,yy,0});
	while(q.size()){
		node f=q.top();
		q.pop();
		for(int i=0;i<4;i++){
			int x=f.x+dx[i],y=f.y+dy[i];
			if(x>=1&&x<=n&&y>=1&&y<=m){
				int as=ans[f.x][f.y];
				if(a[x][y]!=a[f.x][f.y])as++;
				if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
			cout<<ans[i][j]<<" ";
		cout<<endl;
	}
	return 0;
}

6. 和谐分组

讲解二分

终于是正常一点的题目了,并且是我最喜欢的算法(蒟蒻最喜欢的算法是并查集、二分、dp、LCA)

二分是确定一个答案然后对其分析,而答案常常有这样一种情况:

左闭右开

​ 左闭右开

​ 右闭左开

题目通常会让我们找符合条件的最大值或最小值。

以这道题为例,就是去在可能的谐度度中找一个最小的。

比答案大的都可以,不答案小的都不可以。

这个我们叫右闭左开。

而比答案小的都可以,不答案大的都不可以。

这个我们叫左闭右开。

二分顾名思义,就是二分。

左闭右开:

int l=0,r=INT_MAX/2;
while(l+1<r){
	int mid=(l+r)>>1;
	if(check(mid))l=mid;//这里不同
	else r=mid;//这里不同
}

右闭左开:

int l=0,r=INT_MAX/2;
while(l+1<r){
	int mid=(l+r)>>1;
	if(check(mid))r=mid;//这里不同
	else l=mid;//这里不同
}

分析

现在我们已经会了二分,我们可以愉快地做这道题了。

我们可以开始写 c h e c k check 函数:

bool check(int x){//check函数顾名思义,就是用来反对我们的答案x是否可行
	int maxn=a[1],minn=a[1],s=1;//初始化
	for(int i=2;i<=n;i++){
		maxn=max(maxn,a[i]);//最大
		minn=min(minn,a[i]);//最小
		if(maxn-minn>x){
			s++;//新分一个组
			maxn=a[i];//初始化
			minn=a[i];//初始化
		}
	}
	return s<=k;//如果组数小于等于k,就说明不可行
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
const int MAXN=1e5+10;
int n,k,a[MAXN];
bool check(int x){
    int maxn=a[1],minn=a[1],s=1;
    for(int i=2;i<=n;i++){
        maxn=max(maxn,a[i]);
        minn=min(minn,a[i]);
        if(maxn-minn>x){
            s++;
            maxn=a[i];
            minn=a[i];
        }
    }
    return s<=k;
}
int main(){
    read(n);read(k);
    for(int i=1;i<=n;i++)read(a[i]);
    int l=0,r=INT_MAX/2;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid;
    }write(r);
    return 0;
}
发布了41 篇原创文章 · 获赞 61 · 访问量 640

猜你喜欢

转载自blog.csdn.net/qq_46230164/article/details/105314105
今日推荐