以难度顺序排列
1.摸鱼王的2468复读机
分析
有规律的,除去第一位2的0次方个位数为1,其余以2 , 4,8 , 6为循环
代码
#include<iostream>
using namespace std;
int main() {
long long n;
cin>>n;
int ans;
if(n==0)
ans = 1;
else {
switch(n%4) {
case 0: ans = 6;
break;
case 1: ans = 2;
break;
case 2: ans = 4;
break;
case 3: ans = 8;
break;
}
}
cout<<ans<<endl;
return 0;
}
2.签到题
分析
其实输出结果就是printf中的表达式,随便尝试一下就可以发现规律就是1,-1重复的循环,所以唯一的问题就是n过大,会超时
代码
#include<iostream>
using namespace std;
int main() {
long long n;
scanf("%lld",&n);
if(n%2==0)
cout<<-1<<endl;
else
cout<<1<<endl;
return 0;
}
3.阶乘
分析
对于所有数,能够相乘得出10的倍数才能使结果的末位增加0,所以5成为一个非常重要的数字,对于每一个5,都需要一个2和它配对相乘才能得出10,所以所有2的倍数(2,4,6,8)都能够担此重任,把所有数分成5个一组,而对于阶乘而言,每一个5之前都会有至少两个2的倍数,所以一定够用。
需要注意的地方在于一些比较特殊的数字,例如25=5 * 5,这个数字能够拆分成为两个5,所以可以增加两个0,而50 = 2 * 5* 5也可以增加两个0,125 = 5 * 5 * 5,可以增加三个0,所以对于每一个数n!末尾的0应该表示为f(n) = n/5 + f(n/5),并且循环嵌套。
代码
#include<iostream>
using namespace std;
int main() {
int n;
int count=0;
cin>>n;
while(n) {
n /= 5;
count += n;
}
cout<<count<<endl;
return 0;
}
4.ff爱组合数
分析
问题其实已经给出了公式,最复杂的部分就是怎么解决时间复杂度和答案溢出的问题,直接两个for循环嵌套就可以解决,然后计算结果的时候多多mod取余,以防溢出,虽然题目中好像没有这种情况。
代码
#include<iostream>
#include<string.h>
using namespace std;
const long long MOD = 1e9 + 9;
long long num[2002][2002];
void fun();
int main() {
int T;
memset(num,0,sizeof(num));
cin >> T;
fun();
while (T--) {
int n, m;
cin >> n >> m;
cout << num[n][m]<< endl;
}
}
void fun() {
for (int i = 0; i <= 2001; i++) {
num[0][i] = 0;
num[i][0] = 1;
}
for (int i = 1; i <= 2001; i++) {
for(int j = 1; j <= i; j++) {//因为i一定大于等于j,结束条件减少无意义的计算
num[i][j] = (num[i-1][j]%MOD + num[i-1][j-1]%MOD) % MOD;
}
}
}
5.合并滑稽
分析
是一道贪心策略的题目,我觉得贪心没有固定的解题模式,就是一种理解方式。
本题的意图就是不断把一组数排序,然后取最小的两个数相加得出一个新的数放入数列中,之前的两个数抛去,并将相加的结果累加得出答案。
根据分析,其实用最简单的数组就能写,但是数据最大开到了10000,我算了一下,时间复杂度大概是10的8次方,亿级别以上基本不可能过,但是学校的平台上数据水了竟然过了。
所以必须使用优先队列,这里涉及STL内容(以后总结的话会更新)。首先将每个元素压入小根堆,在循环的时候不断的取头上的值,取两个作为最优值(a,b),最后再弹掉。结果就是不断累加两个元素(a,b),最后再把此次的和再压入小根堆。
错误代码
这是会超时的代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int num[10005];
int main() {
int n;
cin >> n;
//memset(a,0,sizeof(a));
for (int i = 0; i < n; i++) {
cin >> num[i];
}
long long mx = 0;
while (n > 1) {
sort(num, num+n);
num[0] += num[1];
mx += num[0];
n--;
for (int i = 1; i < n; i++) {
num[i] = num[i+1];
}
}
cout <<mx<<endl;
return 0;
}
AC代码
#include<queue>
#include<iostream>
#include<stdio.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;//优先队列定义小根堆
int main(){
int n,x;
int ans=0;//一定记得初始化,因为没初始化卡了两次
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
q.push(x);
}
while(q.size()>=2){//终止条件
int a=q.top();//取头最优值
q.pop();//丢弃
int b=q.top();
q.pop();
ans += a+b;//最优值来源
q.push(a+b);//压入
}
printf("%d\n",ans);//输出
return 0;
}
6.约数的个数
分析
这道题的意图就是求出1-N中所有数的约数个数,并依次相乘一个常数,最后求出相加的和,且为了防止数字过大溢出要约1e15,。
求约数可以使用数论中提到的方法–筛法求解,
代码
在这里插入代码片
7.猜猜在哪里
描述
有一个很有意思的魔术
桌子上有从左到右编号为1到n的n顶不透明的帽子,一开始只有1号帽子下面有一只兔子,魔术师会依次交换其中的m对帽子,也就是说每次把从左往右的第x个帽子和第y个帽子交换,其中x, y不相同,帽子下的兔子也会跟着帽子移动。
由于魔术师神奇的手法,作为观众的你没有看到魔术师的部分交换操作,但你按顺序记下了一部分不一定连续的交换操作。对于每个你没看到的交换操作,我们假设魔术师会等概率地选择两个位置不同的帽子交换,请问所有操作结束以后,兔子在从左到到右第1, 2, …, n号帽子底下的概率分别是多少?