这篇博客汇总了我在12月比赛中错误的思维题,出于他们没有固定的分类,将他们进行了汇总。
第一题
蒲煜凡学长最近学习了素数的许多性质,于是他养成了一种怪癖,只会选择素数,对合数视而不见
自从他在CCPC省赛中获得金牌后,就一直答应要带ACM算法协会的成员聚餐,这一天终于到来了!
他带领着所有成员来到了学校的餐厅,正当所有人准备点菜的时候,他大喝一声:只能选择价格为质数的菜肴!
作为ACM算法协会未来的成员,现在请你来计算一下,蒲煜凡学长一共有多少种点菜策略呢
众所周知,由于蒲煜凡学长特别抠门,所以如果看到价格特别高,他也有可能一道菜都不点哦~
由于答案可能会比较大,最后答案需要对1e^9 + 7取余
输入描述:
第一行包含一个整数n,1<=n<=1e^3,
第二行有n个数组,每个数字的范围是1∼10^8
示例1
输入
6
1 344 34 17 5 2
输出
8
题目分析,主要考察如何更高效的求解质数(利用求平方)
以及集合内元素的组合方式 听起来很简单当时我并不会
细节说明还是习惯性的写在了备注里,代码如下。
#include<stdio.h>
int main()
{
long long a[1020];
int n;
scanf("%d",&n);
int count=0;
for(int i=0;i<n;i++)//输入步骤
{
scanf("%lld",&a[i]);
int flag=0;
for(int j=2;j*j<=a[i];j++)//多了一个等号就过了
//注意A
{
if(a[i]%j==0)//不是素数
{
flag=1;
break;
}
}
if(flag==0&&a[i]!=1)//别被
{
printf("%d",a[i]);
count++;
}
}
int sum=1;
for(int i=1;i<=count;i++)
{
sum=(sum*2)%1000000007;//取余的美妙方法(dogs)
//注意B
}
printf("%d",sum);
return 0;
}
有两个注意事项
A:注意一定要加上等号!
在非平方法处理时,我们的确没有必要计算最后一个数,但是,在平方法处理时,不加等号就有可能丢掉它的质数
eg:3*3=9,如果不加等号,那么9会被识别成一个质数
B:关于取余!
一定要在过程中取余,不然很可能会爆。
最后附加一个高中知识
如果一个集合内有个元素,那么组合方式为n方,这里有多少种菜,就是有多少种菜的平方个点菜个数。
愉快的第二题
大多数情况下甘靖学长说普通话时都很标准,但有时却令人抓狂,这件事困扰他很久了,突然有一天,他想通过读数字来纠正他的发音,
但是他不想按正常的顺序读数字,于是他按照某种规律写了一些数字在黑板上,比如如果他想数到9,那么他会把数字写成如下格式:
1 2 6 7
3 5 8
4 9
如果他想数到15,那么他会把数字写成如下格式:
1 2 6 7 15
3 5 8 14
4 9 13
10 12
11
他突然发现他写数字的规律是个蛇形,具体描述是这样的:
对于每一条左下-右上的斜线,从左上到右下依次编号1,2,…,2n-1;按编号从小到大的顺序,将数字从小到大填入各条斜线,其中编号为奇数的从左下向右上填写,编号为偶数的从右上到左下填写
输入描述:
输入一个不大于10000的正整数n,表示要填充到的数字的大小
输出描述:
按规律输出这些数字,相邻两个元素之间用单个空格间隔
示例1
输入
50
输出
1 2 6 7 15 16 28 29 45 46
3 5 8 14 17 27 30 44 47
4 9 13 18 26 31 43 48
10 12 19 25 32 42 49
11 20 24 33 41 50
21 23 34 40
22 35 39
36 38
37
这个题最开始我想到的就是通过不断判断停止的点来结束一行的输出,所以我写了大概100多行代码还没写完。
当然,这不是重点!
这个题的正解方式是通过斜向输入横向输出,这样既可以方便输入时没有那么多判断,也可以输出时不太费脑筋。
#include<stdio.h>
int main()
{
int a[200][200]={0};
int n;
scanf("%d",&n);
int hang=1;
int temp=n;//为了保存n的值
while(temp-hang>0)//判断有多少斜着的行
{
temp-=hang;
hang++;
}
//对temp进行新的定义
//使之成为中间变量
temp=1;
for(int i=0;i<hang;i++)
{
//int j;//作为关系的标准
//对数据进行存储
//重点A
if(i%2==0)//判断从下向上
{
for(int j=0;j<=i;j++)
{
a[i-j][j]=temp;
temp++;
if(temp>n)
goto loop;
}
}
else//判断从下向上
{
for(int j=0;j<=i;j++)
{
a[j][i-j]=temp;
temp++;
if(temp>n)
goto loop;
}
}
}
loop:;//赋值到50可以截止了
for(int i=0;i<hang;i++)//横向输出
{
for(int j=0;j<hang;j++)
{//重点B
if(a[i][j]!=0)
{
printf("%d",a[i][j]);
if(a[i][j+1]!=0||j!=hang-1)
printf(" ");
if(j==hang-1)
printf("\n");
}
else
{
printf("\n");
break;
}
}
}
return 0;
}
重点A:这个题是蛇形的赋值模式,所以一定要注意到底是从下往上赋值还是从上往下赋值。
重点B:这个题的核心是通过建立一个全是0的二维足够的的数组,然后一个个赋值,最后全面输出,但0并不是我们想要的,所以我们一碰到0就进行换行,就可以是心啊我们想要的输出了。
甜甜的第三题
蒲煜凡学长跟学妹出来约会了!蒲煜凡学长作为一名标准直男,想要给学妹买一杯奶茶,便问学妹喜欢喝什么样的奶茶,学妹回了两个字
随便!
蒲煜凡学长傻眼了,但作为一名优秀的ACMer,他是不会坐以待毙的,他早早便将学妹喜欢喝的奶茶价格和奶茶店的价格表弄到了手!现在你要根据奶茶价格,判断学妹到底喜欢喝哪一种奶茶,如果有多种奶茶价格都符合要求,输入种类靠前的一种
输入描述:
第一行包含一个整数n,表示有n种奶茶
第二行有n个数字a[i],a[i]表示第i种奶茶价格
第三行包含一个整数Q,表示Q次询问
第四行包含Q个整数x[i],表示学妹喜欢喝的第i杯奶茶的价格,如果没有找到,则输出-1
输出描述:
输出Q行,每行一个整数表示答案
示例1
输入
6
1 2 10 5 3 2
1
3
输出
5
示例2
输入
5
3 1 7 5 8
2
6
7
输出
-1
3
这个题涉及到一个知识点,就是map,由于我菜 没学过C++,对map的基本用法还没有足够的了解,但是为了节省存储空间,就用上了。
手动分割线
map是一种映射,可以建立不同数据类型的映射,其实有点像数组,这里我需要的数据类型都是int,于是它就更像数组了。
map<int,int>pos;
这里定义了一个int型对int型的映射,并把它命名为pos。
这样我们就没有必要开辟像奶茶价格版的空间大小了。
然后我们回归正题
说一说这个题的思路。
通过建立一对一的映射储存奶茶和价格,然后储存各各奶茶的价格和位置,通过if判断来避免重复(因为只需要第一杯奶茶)。这样map第一个对应的是价格,第二个对应的是位置。
查找起来就方便多了。
最后附上代码。
#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<int,int>pos;
long long pri[100000];
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)//历遍计算出每个花的位置
{
scanf("%lld",&pri[i]);
if(pos[pri[i]]==0)
pos[pri[i]]=i+1;//pri是奶茶的价格,pos是奶茶的位置
}
int ask;
scanf("%d",&ask);
for(int i=0;i<ask;i++)//完成各种奶茶的问题
{
long long ans;
scanf("%lld",&ans);
if(pos[ans]==0)//这个价格还没有奶茶
printf("-1\n");
else
printf("%lld",pos[ans]);
}
return 0;
}
发财的第四题
由于zhb非常喜欢买手办,但是他非常贫穷,所以他想通过炒股来赚钱买手办!
给定一个长度为N的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格,请计算zhb所能获得的最大利润。zhb可以尽可能的完成更多的交易(多次买卖一支股票)。
注意:zhb不能同时参与多笔交易(zhb必须在再次购买前出售掉之前的股票)。
输入
第一行包含整数 N,表示数组长度。
第二行包含 N 个不大于 100000 的正整数,表示完整的数组。
1 ≤ N ≤ 10^5
输出
输出一个整数,表示最大利润。
样例输入
6
7 1 5 3 6 4
样例输出
7
这个题看起来比较简单 ,但是如何尽可能减少逻辑的复杂程度去实现这个程序还是很重要的。(超时的泪)
如果我们正着去想这个问题,那么就需要考虑什么时候是买入的最优解,这让我们需要历遍很多次,非常容易超时而且还可能 大概率出错。
那么我们应该怎么去想呢,应当反过来去思考,如果后面的股价比前面的高那就会有一个购买过程。
重点来了:这样写程序可以避免找最优解的过程,就是说只要股票的价格后面的比前面的高,就完成做差。(因为这个时间段肯定不会去卖股票),这样也不用考虑买卖股票的手续步骤,一旦股价长了起来就是前面的高于了后面的这时候我肯定没有从高处购买,但是我可能在高出完成了卖出的手续,这就需要在历遍的时候注意,不能重复历遍,(如果从后往前看)每一次循环都是从一次股价高的地方开始的。
代码如下
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
int a[100000];
for(int i=0;i<n;i++)
{
scanf("%d",&a[n-i-1]);//逆序输入,方便反向去找股价
}
int sum=0;
for(int j=0;j<n-1;j++)
{
int count=0;
for(int k=j+1;k<n;k++)
{
if(a[j]>a[k])
{
count+=a[j]-a[k];
j=k;//不去重复历遍
}
else
break;
}
sum+=count;
}
printf("%d",sum);
return 0;
}
收尾的花朵题目
蒲煜凡学长最近迷上了一个漂亮的小学妹,他想送一朵特别的花给这个小学妹。
他现在有一把N朵漂亮的花,每多花都有a[i]个花瓣,如果有1朵花的花瓣数与其他任意一朵花的花瓣数之差等于2,那么他可以选择把这朵花扔掉,也可以选择丢弃。
请问经过若干次操作之后蒲煜凡学学长能找到唯一的一支特别的花送给小学妹吗?
输入描述:
第一行一个整数t(1<=t<=500)表示测试样例数
每个测试样例第一行一个整数n(1<=n<=1e4)表示花的数量,第二行n个整数ai表示花瓣数。
输出描述:
输出t行"YES"or"NO"
示例1
输入
1
4
2 4 6 8
输出
YES
示例2
输入
1
5
9 3 5 6 7
输出
NO
这个题的基本思路就是用map做一个查重,然后排序结束后,查找下一朵花是否比上一朵花大2,如果大2,就可以消去。
这样看起来并不难 ,但是!有一个点非常重要,如果只有一种瓣数的花那么应该怎么处理,如果这一支花是唯一的,那么应当是可以的,如果不唯一,那么一定不行。
#include<cstdio>
#include<map>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
long long n;//循环次数
map<long long,long long>ban;
long long a[100000]={0};//储存花的瓣数
scanf("%lld",&n);
for(long long k=0;k<n;k++)
{
int flag=0;
ban.clear();//对map清空,便于新的开始
long long account;
scanf("%lld",&account);
long long judge=0;
long long t=0;
int count=0;
for(long long j=0;j<account;j++)
{
scanf("%lld",&t);//输入每一只花的瓣数
if(ban[t]==0)//利用map进行了查重
{
a[judge]=t;//对花的瓣数进行查重
ban[t]=1;//实现了查重
judge++;//判断后面有几只花
}
count++;
}
if(judge==1)//只有一种瓣数的花
{
if(count>1)//如果有很多朵
printf("NO\n");
else//如果有且只有一朵
printf("YES\n");
goto loop;
}
sort(a,a+judge);//从小到大对花进行排序
for(int j=0;j<judge-1;j++)
{
if(a[j]!=a[j+1]-2)//如果不大二必然不可以
{
flag=1;
break;//那我就跳出循环
}
}
if(flag==1)
printf("NO\n");
else
printf("YES\n");
loop:;//直接结束了本次循环
}
return 0;
}
备注非常详细,全是泪啊。
还是我太菜了,需要努力。
return code;