概述
问题 A: 互异字符串
题目描述
请实现一个算法,确定一个字符串的所有字符是否全都不同。
给定一个字符串,请返回一个True代表所有字符全都不同,False代表存在相同的字符。
输入
输入一个字符串。保证字符串中的字符为ASCII字符。字符串的长度小于等于3000。
输出
如果所有字符全都不同输出“True”,如果存在相同的字符则输出“False”。
样例输入
aeiou
BarackObama
样例输出
True
False
思路:
因为字符数量不多,可以用数组进行标记,遍历一遍字符串,如果出现相同的字符就break
#include<bits/stdc++.h>
using namespace std;
char s[3005];
int main()
{
int mp[305];
while(~scanf("%s",s+1)){
memset(mp,0,sizeof(mp));
int ls=strlen(s+1),ju=1;
for(int i=1;i<=ls;++i){
if(mp[s[i]]==0){
mp[s[i]]=1;
}else{
ju=0;
break;
}
}
if(ju){
printf("Truen");
}else{
printf("Falsen");
}
}
}
问题 B: 第k个数
题目描述
有一些数的素因子只有3、5、7,请设计一个算法,找出其中的第k个数。
输入
给定一个正整数k,保证k小于等于100。
输出
输出第k个数。
样例输入
1
3
4
5
样例输出
3
7
9
15
思路:
最容易想到的就是暴力的写法,遍历3到一个很大的数字,判断是是否只由3,5,7相乘得到
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int ans[maxn],la;
int main()
{
int k,m;
for(int i=3;i<maxn;++i){
m=i;
while(m%3==0){
m/=3;
}
while(m%5==0){
m/=5;
}
while(m%7==0){
m/=7;
}
if(m==1){
ans[++la]=i;
}
}
while(~scanf("%d",&k)){
printf("%dn",ans[k]);
}
}
也可以这样看,每个数都可以分别乘以3,5,和7(当然会有重复)也就是有三个分支
令根节点为1…就得到一颗三叉树(由于画得太丑就不放出来了),这个树中每个节点的数都符合要求,现在的问题就是如何能知道哪个数是第k大的
可以利用优先队列,将初始的3,5,7放到优先队列中(我们自定义一下,小的在上),每次取出最小的那个数(定为x),再将x乘3,乘5,乘7分别加入优先队列中…循环
显然我们第一次取出的数便是第一小的,第二次便是第二小的…
第k次取出的数便是答案
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int main()
{
int k;
while(~scanf("%d",&k)){
priority_queue<ll,vector<ll>,greater<ll> >pq;
pq.push(3),pq.push(5),pq.push(7);
ll x;
while(k--){
x=pq.top();
while(pq.top()==x){//可能有重复的数
pq.pop();
}
pq.push(x*3);
pq.push(x*5);
pq.push(x*7);
}
printf("%lldn",x);
}
}
当然也可以对上面的思路进行简化(由于确实不太好解释简化的思路就偷一下懒…哈哈)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int ans[maxn];
int main()
{
int n;
while(~scanf("%d",&n)){
memset(ans,0,sizeof(ans));
ans[0]=1;
int a=0,b=0,c=0;
for(int i=1;i<=n;++i){
int x=min(ans[a]*3,min(ans[b]*5,ans[c]*7));
ans[i]=x;
if(x==ans[a]*3){
++a;
}
if(x==ans[b]*5){
++b;
}
if(x==ans[c]*7){
++c;
}
}
printf("%dn",ans[n]);
}
}
问题 C: 2的个数
题目描述
请编写一个程序,输出0到n(包括n)中数字2出现了几次。
输入
输入一个正整数n。
输出
输出0到n的数字中2出现了几次。
样例输入
2
10
20
样例输出
1
1
3
思路
直接枚举2到n,循环计算2的数量(暴力大法好)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int main()
{
ll n,m,ans;
while(~scanf("%lld",&n)){
ans=0;
for(ll i=2;i<=n;++i){
m=i;
while(m){
if(m%10==2){
++ans;
}
m/=10;
}
}
printf("%lldn",ans);
}
}
问题 D: 外星人的语言
题目描述
Kimi费了很大劲,终于和地外文明联系上。
我们地球人通常有10根手指,因此我们习惯用10进制的数,而外星人的手指有16跟、8根等不等的数目,因此他们使用与我们不同的进制。
为了方便沟通,需要你开发一款工具,把地球人的10进制转换成外星人的R进制形式。
输入
输入有多行。
每行包括两个正整数n和R,其中2≤R≤16。
输入直到文件结束为止。
输出
对于每个用例,输出n对应的R进制形式。
超过10进制的数,10用A表示、11用B表示,依次类推。
样例输入
1989 2
1119 16
样例输出
11111000101
45F
思路
算是简单的进制转化
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int ans[maxn],la;
int main()
{
ll n,r,m;
while(~scanf("%lld %lld",&n,&r)){
la=0,m=n;
while(m){
ans[++la]=m%r;
m/=r;
}
for(int i=la;i>=1;--i){
if(ans[i]>9){
printf("%c",'A'+ans[i]-10);
}else{
printf("%d",ans[i]);
}
}
putchar('n');
}
}
问题 E: 3n+1猜想
题目描述
卡拉兹(Callatz)猜想:
对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步
得到n=1。卡拉兹在1950年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,
结果闹得学生们无心学业,一心只证(3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过1000的正整数n,简单地数一下,需要多少步(砍几下)才能得到n=1?
输入
每个测试输入包含1个测试用例,即给出自然数n的值。
输出
输出从n计算到1需要的步数。
样例输入
3
样例输出
5
思路
按题目讲的来就行
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int ans[maxn],la;
int main()
{
ll n;
while(~scanf("%lld",&n)){
int ans=0;
while(n>1){
if(n&1){
n=(3*n+1)/2;
}else{
n=n/2;
}
++ans;
}
printf("%dn",ans);
}
}
问题 F: 狗狗大决杀
题目描述
这是 10 月 32 日发生的事情。抽筋流高手布置了一个非常 BT 的作业。他说要在一定时间内把屏幕上的所有小狗布成一条直线。而这个时间非常之短!!!众人大呼 BT,但是抽筋流高手是非常严格的,所以大家只好遵从他的命令。
众所周知,只有达达的 APM 在 190 以上,所以最后完成作业的只有他一个。他受到了 抽筋流高手的表扬。
然而罗小松觉得不爽,于是他趁达达去 WC 的时候在那一条直线上放了一系列机枪兵, 对可爱的狗狗们进行了残忍的屠杀。
已知个机枪兵有一定的射程。然而这是一种特殊的星际版本,机枪升攻击的时候也 可以升射程!所以每个机枪的射程不一定相同。现在一列狗兵排成直线,一次一个机枪兵打掉一定范围内的所有狗狗,每个狗狗有一个编号,每次给出一个杀伤区间, 其中的小狗将会被全部杀掉。最后,让你统计一共杀了多少个。
简而言之,给出数轴上 N 条线段,条线段用两个数表示 A , B(-109<=A<B<=109), 表示从 a 到 b 的一条线段。现在请求出它们覆盖数轴上的多长距离。
输入
第一行:N (N ≤ 20000)。
以后 N 行,行两个数:Ai Bi 。
输出
一个数,表示覆盖长度 。
样例输入
3
2 8
-1 1
5 10
样例输出
10
思路
线段覆盖问题…很明显可以用线段树或者树状数组来做
当然这个题用结构体排序也就可以了(线段树,树状数组写法就…嘿嘿)
将所有线段按右坐标大小降序排序,再遍历
用一个变量m表示当前的位置(初始化为最右的端点位置)
从左到右遍历的过程中有几种情况(a,b表示左右端点)
a m b
a到m这段是被覆盖的,累加,再将m变为a-1
a b m
a到b这段是被覆盖的,累加,再将m变为a-1
m a b
a到b区间已经被统计过了,忽略
最后输出就可以
(如果难以理解可以在纸上模拟一下计算)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
struct node{
ll a,b;
bool operator<(const node x)const{
return b>x.b;
}
};
node w[maxn];
int main()
{
int n;
while(~scanf("%d",&n)){
ll ans=0,m=0;
for(int i=1;i<=n;++i){
scanf("%lld %lld",&w[i].a,&w[i].b);
--w[i].b;//由于题目对于1-2-3只覆盖两个格子,将右端点减一方便计算
m=max(w[i].b,m);
}
sort(w+1,w+n+1);
for(int i=1;i<=n;++i){
m=min(m,w[i].b);
if(m>=w[i].a){
ans+=m-w[i].a+1;
m=w[i].a-1;
}
}
printf("%lldn",ans);
}
}
问题 G: 折纸
题目描述
小s很喜欢折纸。
有一天,他得到了一条很长的纸带,他把它从左向右均匀划分为N个单位长度,并且在每份的边界处分别标上数字0~N。
然后小s开始无聊的折纸,每次他都会选择一个数字,把纸带沿这个数字当前所在的位置翻折(假如已经在边界上了那就相当于什么都不做)。
小s想知道M次翻折之后纸带还有多长。
输入
第一行包含两个正整数 N 和 M ,表示纸带的长度和操作的次数。(N ≤ 10^18,M ≤ 3000)。
接下来的一行包含 M 个整数 Di ,其中 Di 表示第 i 次选择的数字。
输出
输出文件只有一个数字,即纸带最后的长度。
样例输入
5 2
3 5
样例输出
2
思路
样例或许看不出什么
以
5 2
3 2为例(我们向右翻折)(向左翻折同理)
0 1 2 3 4 5
以3为折痕得到
2 1 0
3 4 5 这里其实可以看成是3 4 5 6
再以2为折痕得到
3
2 1 0
4 5 这里其实可以看成是4 5 6
也就是说以3为折痕翻折后,以2为折痕翻折相当于以4为折痕翻折(因为4和2重合了)
可以发现,在折纸的过程中,每次翻折,部分点的位置也会发生变化,由于m很小,可以不停用循环模拟这种变化
比如以x为折痕在x左边的点a会变到右边的b位置,a和b的关系为b-x=x-a化简也就是b=2*x-a
a x b
由此可以知道每一个点的变化位置
那么用变量维护左端点和右端点,遍历折痕点,模拟出点的位置变化,最后得到的左右端点的距离就是纸带的长度
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
ll a[3005];
int main()
{
ll n,x,l,r;
int m;
while(~scanf("%lld %d",&n,&m)){
l=0,r=n;
for(int i=1;i<=m;++i){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;++i){
for(int j=i+1;j<=m;++j){//坐标变化(从i+1开始是因为之前的折痕已经没有意义了)
if(a[j]<a[i]){
a[j]=a[i]+a[i]-a[j];
}
}
r=max(r,a[i]+a[i]-l);
l=a[i];
}
printf("%lldn",r-l);
}
}
问题 H: 加一
题目描述
你的任务是很简单,给你一个非负整数 N,输出 N+1。
唯一的复杂之处在于给出的整数是一个 2~36 进制(包括边界)中一个未知进制的整数。(大家知道的,从 10 开始的数字分别用 A,B,C,……来表示)
因此,你的程序必须按字典序递增输出所有可能的结果。
输入
一行,包含一个由数字 0 至 9 与大写字母 A 至 Z 组成的整数 N,数据保证没有前导零。 (N包含1~200位数字)
输出
输出所有的可能结果,每个结果占一行。
样例输入
9
样例输出
10
A
思路
他给出的整数可以写成abcdef…这种形式(可能是2-36进制中的任意一个)
假设d是abcdef中最大的那个数,那这个整数的进制就不能小于等于d,否则就进位了
我们获取这个整数最大的位数,就知道了这个整数可能的进制
也就是d+1到36进制,仔细想想我们只需要考虑d+1和d+2进制就行
(d+2进制到36进制都是一样的,只需要加一就行,不能进位)
(因为此时最大的数d加一也不满足进位条件d+1 < d+2 <…< 36)
也就是说
末位加一,就只有两种情况,进位或者不进位
如果末尾是最大的就表示可以进位
否则就不能进位
(当然有一种特殊情况,如果最低位的数字是Z就只能进位)
按这个思路写下来代码不短,注意一些细节就行
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
char s[maxn];
int a[maxn],b[maxn];
char ans1[maxn],ans2[maxn];
int main()
{
ll n,m,ls,jz;
while(~scanf("%s",s+1)){
int ls=strlen(s+1),jz=1;
for(int i=1;i<=ls/2;++i){//字符串转置
swap(s[i],s[ls-i+1]);
}
for(int i=1;i<=ls;++i){//化为数字
if(s[i]>='A'&&s[i]<='Z'){
a[i]=s[i]-'A'+10;
}else{
a[i]=s[i]-'0';
}
b[i]=a[i];
}
for(int i=1;i<=ls;++i){//获取每一位上最大的数
jz=max(jz,a[i]);
}
if(jz==a[1]){//如果最低位是最大的就可能进位,也就是可能有两个答案
int lb=ls,la=ls;
++a[1],++b[1],a[la+1]=0,b[lb+1]=0;
for(int i=1;i<=la;++i){//进位
if(a[i]>jz){
a[i]-=jz+1;
++a[i+1];
}
}
if(a[la+1]!=0){//如果最高位进位了,就把长度加一
++la;
}
for(int i=la;i>=1;--i){
if(a[i]>9){
ans1[la-i+1]=a[i]+'A'-10;
}else{
ans1[la-i+1]=a[i]+'0';
}
}
for(int i=lb;i>=1;--i){
if(b[i]>9){
ans2[lb-i+1]=b[i]+'A'-10;
}else{
ans2[lb-i+1]=b[i]+'0';
}
}
ans1[la+1]='