CSP-J省一班夏令营模拟赛二补题报告

目录

一、AC情况

二、赛中概况

三、解题报告

问题一:平整(smooth)

情况:

题意:

题解:

正解代码:

问题二:字符串统计(statistic)

情况:

题意:

题解:

正解代码:

问题三:从a到b(reach)

情况:

题意:

题解:

正解代码:

问题四:粉刷栅栏(color)

情况:

题意:

题解:

正解代码:

四、总结


一、AC情况

全部四道题赛后补题AC

二、赛中概况

前两题做的很快,到第三题卡住,在最后把第四题暴力写上了。

前两题数组都开小了,第三题思路错误,第四题暴力拿了30分。

三、解题报告

问题一:平整(smooth)

情况:

赛后补题AC

题意:

给定一个序列,求有多少个数x满足:其减去序列中每个数的差的绝对值小于等于1。

比赛时很快就想好做出来了,但是数组开小了(开数组时少打了一个0),导致没有拿全分。

题解:

思维题,找到给出的序列中的最大最小值,分类讨论:

1.若最大值和最小值的差大于2,如1,2,3,4,5,6这组数,不难看出没有x满足题目条件,输出0;

2.若最大值和最小值的差等于2,如1,2,3,1,2,3这组数,满足题目条件的x只有1个,该序列中为2;

3.若最大值和最小值的差等于1,如1,2,1,1,2,1这组数,x有两个值能够满足题目条件,在该序列中x的两个取值分别为1和2;

4.若最大值和最小值的差等于0,即最大值等于最小值,就说明该序列中所有数都相等,那么x有3种可能满足题目条件。设该序列中所有数都是a,x的三种可能值分别是a-1、a、a+1。

这样我们就可以很轻松的做出这道题了。

正解代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,a[100050];
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		int maxx=-1,minn=1e9+7;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			maxx=max(maxx,a[i]);
			minn=min(minn,a[i]);
		}
		if(maxx-minn>2){
			printf("0\n");
		}
		else if(maxx-minn==2){
			printf("1\n");
		}
		else if(maxx-minn==1){
			printf("2\n");
		}
		else{
			printf("3\n");
		}
	}
	return 0;
}

问题二:字符串统计(statistic)

情况:

赛后补题AC

题意:

给定一个字符串序列并进行查询,每次查询输出[L,R]范围内以元音字母开头和结尾的字符串个数。

比赛时还是数组开小了导致没有AC。

题解:

不难想到,可以使用前缀和提前统计,对于每次查询输出前缀和数组统计的值即可。

正解代码:

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int n,m;
string s;
long long sum[100050];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		cin>>s;
		int len=s.size();
		len--;
		if(s[0]=='a'||s[0]=='e'||s[0]=='i'||s[0]=='o'||s[0]=='u'){
			if(s[len]=='a'||s[len]=='e'||s[len]=='i'||s[len]=='o'||s[len]=='u'){
				sum[i]=sum[i-1]+1;
			}
			else{
				sum[i]=sum[i-1];
			}
		}
		else{
			sum[i]=sum[i-1];
		}
	}
	sum[n+1]=sum[n];
	scanf("%d",&m);
	int l,r;
	while(m--){
		scanf("%d%d",&l,&r);
		printf("%d\n",sum[r+1]-sum[l]);
	}
	return 0;
}

问题三:从a到b(reach)

情况:

赛后补题AC

题意:

给定两个长度相同并且只由0和1组成的整数,进行不限次数的操作,每次操作选择两个不同的下标i和j,将a[i]修改为a[i]|a[j](按位或),将a[j]修改为a[i]^a[j](异或),判断能否通过这种操作将a转化为b。

比赛时想复杂了,用了暴搜,最后拿了零分。

题解:

这其实也是道思维题,若a和b本来就相等,可以直接输出,否则进行分析,不难发现,其实或和异或两种操作的特点导致只要原本数字a中有1,就不可能把1消掉,而如果原本数字a全是0没有1,也不可能将0转化为1。因此判断,只要a和b都含有1,那么a一定可以变成b,如果a和b中有一个全是0,那么a就不可能变成b。分析出这点,这题就非常简单了。

正解代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
string a,b;  //这里使用string类型更方便判断,也不会消除前导0
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		cin>>a>>b;
		if(a==b){
		    printf("OK\n");
		    continue;
		}
		bool flag1=0,flag2=0;
		for(int i=0;i<n;i++){
		    if(a[i]=='1'){
		        flag1=1;
		    }
		    if(b[i]=='1'){
		        flag2=1;
		    }
		}
		if(flag1&&flag2){
		    printf("OK\n");
		}
		else{
		    printf("No\n");
		}
	}
	return 0;
}

问题四:粉刷栅栏(color)

情况:

赛后补题AC

题意:

给定一个字符串,每个字符表示一种颜色,整个字符串表示整个栅栏,每次可以粉刷连续的多个栅栏。判断最少需要多少次能够将栅栏粉刷成给定的状态。

比赛时因为时间快没了而且没什么思路,于是打了个最简单的暴力,直接将字符串去重后输出长度,最终得了30分。

题解:

使用区间dp。

定义f数组,f[i][j]表示从i粉刷到j的最小次数。

推出状态转移方程:f[i][j]=min(f[i][j],min(f[i+1][j],f[i][j-1]));

枚举区间。

先枚举区间长度,再枚举区间左端点,根据这区间长度和左端点计算出右端点,再枚举区间中的分割点。

具体见正解代码。 

正解代码:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int f[55][55];
string s;
int main(){
    memset(f,0x3f,sizeof(f));  //由于是找最小值,在一开始将f数组赋为极大值
    cin>>s;
    s=" "+s;  //让下标从1开始
    int len_s=s.size();
    for(int i=1;i<=len_s;i++){
        f[i][i]=1;  //初始化,从i点到i点的粉刷次数一定是1
    }
    for(int len=2;len<=len_s;len++){  //枚举区间长度
        for(int l=1;l+len-1<=len_s;l++){  //枚举左端点
            int r=l+len-1;  //根据这区间长度和左端点计算出右端点
            if(s[l]==s[r]){  //左右端点颜色相等
                f[l][r]=min(f[l+1][r],f[l][r-1]);
            }
            for(int k=l;k<r;k++){  //枚举分割点
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
            }
        }
    }
    cout<<f[1][len_s-1];  //由于len_s多记了一位,所以最后输出时要-1
    return 0;
}

四、总结

这次比赛打的不是很好,主要前两题数组开小了导致丢了一百多分,第三题赛后来看其实很简单,赛中有可能做对,至少能拿更多分。还是要注意细节,不要把题想得太复杂,实在没思路就打暴力,说不定能拿更多分。最重要的还是心态,心态好,才能发挥出更好的水平。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值