上海计算机协会11月月赛-丙组解题报告

上海计算机协会11月月赛-丙组解题报告

比赛结束后又测了一遍,与初测结果一致,得分确保无误。

T1-奇偶数的判定

奇偶数的判定
内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定一个整数 n,若 n 是一个偶数,输出 even,若 n 是一个奇数,输出 odd。

输入格式
单个整数:表示 n。

输出格式
单个字符串:表示 n 的奇偶性

数据范围
-1,000,000≤n≤1,000,000−1,000,000≤n≤1,000,000
样例数据
输入:
0
输出:
even
输入:
-1
输出:
odd

题目出处
看起来似乎很简单,但有个坑:如果n是负的奇数,那么n%2==-1!!!而非n%2==1!!!

代码如下(实测100分):

#include <bits/stdc++.h>
using namespace std;

int n;

int main(){
	cin>>n;
	if(n%2==0)cout<<"even"<<endl;//判定是否偶数,避免-1的问题
	else cout<<"odd"<<endl;
	return 0;
}

T2-搭积木

搭积木
内存限制: 256 Mb时间限制: 1000 ms
题目描述
小爱同学想要用积木搭起一个金字塔。为了结构稳定,金字塔的每一层要比上一层多一块积木。即搭建规则如下:

金字塔的第 1 层需要放 1 块积木

金字塔的第 2 层需要放 2 块积木

金字塔的第 3 层需要放 3 块积木

金字塔的第 i 层需要放 i 块积木

现在小爱拿到了 n 块积木,请问他最高可以搭出多少层的金字塔?

输入格式
输入一个正整数 n,表示小爱手中的积木数量

输出格式
输出一个正整数,表示小爱最高能搭的金字塔层数

数据范围
对于 50% 的数据,1 ≤ n ≤ 1,0001≤n≤1,000
对于 100% 的数据,1 ≤ n ≤ 1,000,000,0001≤n≤1,000,000,000
样例数据
输入:
12
输出:
4
说明:
4层金字塔需要1+2+3+4=10块积木,而5层金字塔需要1+2+3+4+5=15块积木,所以小爱在有12块积木的情况下,最多搭4层金字塔

题目出处
其实是考三角形数,搭建第i个金字塔需要i*(i+1)/2个积木。

就有了方法1:(100分)

#include <bits/stdc++.h>
using namespace std;

int n;

int main(){
	cin>>n;
	for(int i=1;i<=100000;++i)
		if(n<i*(i+1)/2){
			cout<<i-1<<endl;
			return 0;
		}
	return 0;
}

方法2:这题用while实现合适(100分)

#include <bits/stdc++.h>
using namespace std;

int n,i;

int main(){
	cin>>n;
	while(n>i*(i+1)/2)++i;
	cout<<i-1<<endl;
	return 0;
}

方法3(实测100分):

#include <bits/stdc++.h>
using namespace std;

int n;

int main(){
	cin>>n;
	for(int i=1;i<=100000;++i){
		if(n<i){
			cout<<i-1<<endl;
			return 0;
		}
		n-=i;
	}
	return 0;
}

T3-最长平台

最长平台
内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定一个整数数列 a_1,a_2, ,…,a_n,请找出最长平台,并输出最长平台的数量(数字相等但位置不同的平台算作不同的平台)。

所谓平台,就是指数列中一段连续的、完全相等的数字,单个数字可以成为一个平台。

输入格式
第一行:单个整数 n
第二行:n 个整数 a_1,a_2,…,a_n

输出格式
两个整数:表示最长平台的长度与最长平台的数量

数据范围
对于 50% 的数据,n≤1000
对于 100% 的数据,n≤500,000
1≤a≤1,000,000
样例数据
输入:
7
2 2 2 1 3 3 3
输出:
3 2
说明:
最长平台为2 2 2或3 3 3
输入:
5
3 1 4 1 5
输出:
1 5
说明:
每个数字单独成一个平台

题目出处
由于数据量并不很大,可以枚举所有数,并统计平台数量(100分):

#include <bits/stdc++.h>
using namespace std;

int n,a[500010],ans,s=1,b[1000010];

int main(){
	cin>>n;
	cin>>a[1];
	for(int i=2;i<=n;++i){//枚举所有数
		cin>>a[i];
		if(a[i]==a[i-1]){//判断是否一致(即是否是平台)
			++s;//是的话长度增加
		}
		else{
			if(s>ans)ans=s;//更新最长值
			b[s]++;//统计
			s=1;
		}
	}
	if(s>ans)ans=s;
	b[s]++;
	s=1;//统计最后一个
	cout<<ans<<" "<<b[ans]<<endl;
	return 0;
}

T4-积木染色

积木染色
内存限制: 256 Mb时间限制: 1000 ms
题目描述
有 n 块积木排成一排,小爱需要给每块积木染色,颜色有 m 种,请问有多少种方法,能使相邻两块积木的颜色均不相同?

输入格式
输入两个正整数n,m

输出格式
输出满足条件的方案数模10^9+7的结果

数据范围
对于 30% 的数据,1≤n,m≤10
对于 60% 的数据,1≤n,m≤1e4

对于 100% 的数据,1≤n≤1e15 ,1≤m≤1e9

样例数据
输入:
3 2
输出:
2
说明:
合法的染色方案有:{1,2,1} {2,1,2}

题目出处
这是一个排列组合,第1个位置有m个填法,第2个位置有(m-1)个填法,第3个位置有(m-1)个填法,…,,第n个位置有(m-1)个填法。

方法1:for循环实现

初学者大都会想到这个方法,易于实现,初测60分。

#include <bits/stdc++.h>
using namespace std;

const int mod=1e9+7;
long long n,ans,m;

int main(){
	cin>>n>>m;
	ans=m;//先乘m
	for(int i=2;i<=n;++i){
		ans*=m-1;//循环乘(m-1)
		ans%=mod;//利用余数定理除以(1e9+7)
	}
	cout<<ans<<endl;
	return 0;
}

方法2:幂取模

CSP-J-T1有这样例子。(递归实现)

#include <bits/stdc++.h>
using namespace std;

const int mod=1e9+7;
long long n,m;//数据规模是long long,要注意

long long f(long long step,long long sq){
	if(step==1)return sq;
	int nod;
	if(step%2==0)nod=f(step/2,sq)*f(step/2,sq)%mod;
	else nod=f(step/2,sq)*f(step/2,sq)%mod*sq%mod;//递归
	return nod;
}

int main(){
	cin>>n>>m;
	cout<<f(n-1,m-1)*m%mod<<endl;//注意要再模一次,血的教训令我明白了一切(否则30分)
	return 0;
}

仍然60分!!!超时了!!!
递归超时了,但内存没爆,说明层数不深,可行。
递归超时怎么办?
答案是记忆化。

方法3:幂取模+记忆化(初测100分)

#include <bits/stdc++.h>
using namespace std;

const int mod=1e9+7;
long long n,m,a[200000000]/*记忆化数组*/,b;

long long f(long long step,long long sq){
	if(step==1)return sq;
	int nod;
	if(step<200000000&&a[step]!=0/*短路表达式,避免下标越界*/)nod=a[step];
	else {
		if(step%2==0)nod=f(step/2,sq)*f(step/2,sq)%mod;
		else nod=f(step/2,sq)*f(step/2,sq)%mod*sq%mod;
		if(step<200000000)a[step]=nod;
	}
	return nod;
}

int main(){
	cin>>n>>m;
	cout<<f(n-1,m-1)*m%mod<<endl;
	return 0;
}

方法4:优化方法2(100分)

细心的读者会看到

if(step%2==0)nod=f(step/2,sq)*f(step/2,sq)%mod;
else nod=f(step/2,sq)*f(step/2,sq)%mod*sq%mod;

中递归f被执行了两遍,也就是说复杂度被无缘无故平方了。
可以改为

#include <bits/stdc++.h>
using namespace std;

const int mod=1e9+7;
long long n,m,b;

long long f(long long step,long long sq){
	if(step==0)return 1;
	long long nod=f(step/2,sq);
	if(step%2==0)nod=nod*nod%mod;
	else nod=nod*nod%mod*sq%mod;
	return nod;
}

int main(){
	cin>>n>>m;
	cout<<f(n-1,m-1)*m%mod<<endl;
	return 0;
}

T5-出栈序列

出栈序列
内存限制: 256 Mb 时间限制: 1000 ms
题目描述
给定一个长度为n的、仅由小写字母组成的字符串,将其按序依次放入栈中。

请问在所有可能的出栈序列中,字典序最小的出栈序列是多少?

输入格式
输入第一行, 一个正整数n
输入第二行,一个长度为n的字符串

输出格式
输出所有出栈序列中,字典序最小的出栈序列

数据范围
对于30%的数据,1≤n≤10
对于60%的数据,1≤n≤1e3
对于100%的数据,1≤n≤1e5

样例数据
输入:
3
yes
输出:
esy
说明:
字符y、e、s依次进栈,所有出栈的可能性有:
{yes}、{yse}、{eys}、{esy}、{sey}
其中 {esy} 的字典序最小

题目出处
看起来很难……
第一个方法是穷举(用dfs),20-30分(就不写了),加上剪枝大概60分。
其次是要仔细分析。发现:

if(栈空&&有待入栈字符||有待入栈字符&&栈顶元素字典序<待入栈最小字典序字符){
	入栈;
}else{
	出栈;
}

所以有了这样的程序(100分):

#include <bits/stdc++.h>
using namespace std;

string st;
int pos,pop;//pop为待入栈字符指针
char a[100010];

int main(){
	cin>>pos>>st;
	a[pos-1]=st[pos-1]; 
	for(int i=pos-2;i>=0;--i)a[i]=min(st[i],a[i+1]);//用O(n)复杂度算后缀最小值
	char ans[1000010];//数组模拟栈
	int top=0;//栈顶指针
	while(top!=0||pop<pos){
		if(top==0/*循环仍继续,说明pop>=pos,所以无须追加另一条件*/||pop<pos&&a[pop]<ans[top]){
			top++;
			ans[top]=st[pop++];//入栈
		}
		else{
			cout<<ans[top];
			top--;//出栈
		}
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaoGuohao2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值