前言:
1.参加一个学校的acm迎新赛在洛谷(www.luogu.com)刷的几道简单算法题
2.本人是大一新生,没有基础,实力很弱,只学了C,所以以下代码基本都是C
3.部分题卡住调试并用中间值纠错好久才发现错误,虽然很菜但很有收获
4.部分题有很多种思路,我的写法可能也比较冗杂不够优化,如果大家有更好的优化方法或者更简单的思路,不管和我一样都是初学算法还是一些大佬,都欢迎大家一起交流学习
目录:
1.P1876 开灯
2.P1059 [NOIP2006 普及组] 明明的随机数
3.P1478 陶陶摘苹果(升级版)
题目:
1.开灯
(1)题目介绍
(2)思路
其实正如题目说明想通了之后这道题就是一道数学题,我们可以发现完全平方数的一个特性,比如16=1*16=2*8=4*4,我们可以发现1和16或者2和8都是不一样的数,但是4和4是一样的数字,也就是说,完全平方数可以表示成两两不一样的数乘积和一个数的平方,而非完全平方数都可以表示成一组或几组两两不一样的数的乘积。我们把这个思路带到这个题,如果编号是16,我们不妨这样理解,对应的灯会被1打开,对应的数16关上,2打开,对应的8会关上,而4只有它自己,所以打开后就不会关上,我们再举一个例子,9=1*9=3*3,1打开,9关上,而3没有对应的数,所以9也开着,其他的数比如10=1*10=2*5,都有两两成对的数而没有单独的数,所以打开后都会有一个人帮它关上。所以规律有了,所有的完全平方数对应的编号的灯都是开着的,那么这个看出来了代码自然就是轻松拿下。
(3)代码
#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
for (int i = 1; i * i <= n; i++)
{
printf("%d ", i * i);
}
return 0;
}
(4)总结
对于一些看起来很复杂的题,我们要善于换不同角度对数据进行分析,也要考虑运用一些数学知识,也会很有用,说不定我们就会突然发现巧妙的解题思路
2.明明的随机数
(1)题目介绍
(2)思路
我首先描述一下我个人写这道题的思路,写这道题我只用了一个数组,先把这输入的数存在数组中并排好序(这里我直接用了C的内置函数qsort,如果有不懂的同学可以自行搜索),接着从数组中第一个数跟后一个比,如果一样就continue回到起始,接着第二个跟它后一个比,如果一样再continue,直到不一样我们就直接打印出来,本来打算提交,但是写完我突然发现还要计数,并且还要打印在数组的前面,所以只好在打印的那个for循环前面再加一次for循环用来计数。
其实呢,写这个代码也遇到了很多困难,刚开始for循环里面我并没有直接想到用continue,而是用了下面这个错误的代码
for (int j = 0; j < N; j++)
{
printf("%d ", x[j]);
if (x[j] == x[j + 1]) j++;
}
这样做如果数据正好都不一样的话结果会是正确的,但是如果一组数据都一样,比如:3333,那么答案显而易见就错了,会打印两次3,这是因为我每次让第一个跟后一个比,如果一样,j++两次,直接跳到第三个,而无法判断第三个跟前面两个是否相等,自然会再次打印,所以第一次提交显而易见RA。
下面是正确的代码。
(3)代码
#include <stdio.h>
#include <stdlib.h>
int cmp(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
int main()
{
int N, finalN = 0, x[101];
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%d", &x[i]);
qsort(x,N, sizeof(x[0]), cmp);
for (int j = 0; j < N; j++)
{
if (x[j] == x[j + 1]) continue;
finalN++;
}
printf("%d\n",finalN);
for (int j = 0; j < N; j++)
{
if (x[j] == x[j + 1]) continue;
printf("%d ", x[j]);
}
return 0;
}
(4)总结
虽然现在写题不够快,代码也不够简单,而且总会犯一些低级的错误,但算法这条路上我们要能够坚持还要学会在错误和每一道题中获得成长,通过观察不同测试点的结果分析数据,或者通过一些打印中间值或者调试的方法帮助我们发现问题积累经验
当然还有其他思路,这里给大家提供一两种其他思路:
第一种:
我们可以定义一个长度为1000的布尔类型的数组,赋值False(如果没有学过布尔类型可以直接定义一个int类型的数组并赋初始值为0,这样做不过是为了节省内存,原理都是一样的),接着接收键盘输入的几个随机数,把每个数字对应的下标的那个数组改成True,比如输入98,就数组名[98] = True(相应的把0改为1即可),这样不管出现一次还是重复出现的数对应的下标对应的数组都是True(如果是int数组就找是1的),我们最后只需要遍历这个数组,找到所有是True 的数组对应的[ ]中的数即可。
第二种:
我们可以定义两个数组,将数据存储在第一个数组中,排好序进行排重,然后赋给第二个数组,接着只需要计算第二个数组中数据的个数并按顺序打印即可。
以上两种思路实现起来也很简单,有感兴趣的大家可以自己实现一下。
3.陶陶摘苹果(升级版)
(1)题目介绍
(2)思路
本来看到这道题有人说要用贪心,但刚学算法对贪心还不是怎么了解,也不知道我的代码算不算贪心。我个人的思路就是先判断陶陶能够够到的苹果,然后把这些苹果按需要的力气从小到大排序,然后陶陶摘到自己没有力气,这个时候把摘到的苹果数打印出来即可。(我这道题的代码比较复杂,用了四个数组,如果大家有更优化的代码欢迎交流)
(3)代码
#include <stdio.h>
int cmp(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
int main()
{
int n, s, a, b,num = 0,count = 0,x1[5010]={0}, x2[5010]={0}, y1[5010]={0}, y2[5010]={0};
scanf("%d%d\n%d%d", &n, &s, &a, &b);
for (int i = 0; i < n; i++) scanf("%d%d", &x1[i], &y1[i]);
for (int i = 0; i < n; i++)
{
if (a + b >= x1[i])
{
x2[num] = x1[i];
y2[num] = y1[i];
num++;
}
}
qsort(y2, num, sizeof(y2[0]), cmp);
for (int sum = y2[0]; sum <= s; sum += y2[count])
{
if (count >= num) break;
count++;
}
printf("%d", count);
return 0;
}
(4)总结
其实这道题我刚开始也没有AC,也是一般的数据还好,但出现一些特殊的测试点错误。
下面是我第一次的错误的代码:
错误的只是因为for循环里面少了句 if (count >= num) break;
num是不考虑体力陶陶可以摘到的苹果个数,因为数组我们是初始化为0的,如果我们摘完能够摘到的苹果后体力没有用完,那么我们后面的y2[count]都是0,每次加上都还是小于s,自然会一直对count进行++直到整个数组结束,所以几次运行我都看着屏幕上的5010百思不得其解。为了这一个特殊问题在经历了一个多小时的调试苦想,我终于发现了它。于是我加上了这句if来进行判断。
感想:
这是人生的第一篇博客,用来记录初学算法的带来的思考,算法的这条道路很难,打acm的道路更难,也许学习努力几年后我没有任何比赛牌子或者什么竞赛荣誉,但我认为我也能收获很多,每次冥思苦想后解答出来的题目带给的新知识,逻辑思维的提高,屏幕上的绿色AC,与志同道合的朋友交流思路,互相鼓励,这些事情都会让我非常兴奋,更有动力。学习算法对我来说也许并不只是为了去取得什么荣誉,而是我真正收获了什么。当然不是让大家一股脑啥都不管就要去acm,而是大家要清楚自己想要的到底是什么,我在学习算法之外也简单学习了解了一点单片机和网络安全相关的知识,更充实了我的大一新生活。希望大家每个人都能收获大大的AC!