做一道题的基本步骤

作为一个合格的编程爱好者,刷题是必不可少的。那么,我们应该如何去完成每一道题呢?下面我就告诉你做一道题的基本步骤。

这里我们以去年小学组省赛第三题(Topscoding)为例。

第一步:读题

读题无疑是很重要的一步。认真、仔细的读题可以帮助我们更深刻的理解题意,使我们能更快、更高效的完成代码。

我们来看一下这题的题面(标题省略):


本题请以文件输入输出方式进行提交,输入输出文件名是 string.in / string.out

题目背景

小可可和小多在研究数字串,他们在研究一个数字串的所有子串。

问题描述

给定一个长为 N 的数字串(即由若干 0 ∼ 9 的数字构成的字符串),请你回答有多少连续子串(即从该串中选出连续的若干个数字,可以包括前导 0 是 4 或 5 的倍数。如果同时是 4 和 5 的倍数,应当只被计算一次)。

输入格式

输入文件名为 string.in

第一行一个正整数 N,代表数字串的长度。

第二行一个长为 N 的数字串。

输出格式

输出文件名为 string.out

一行一个正整数,代表满足条件的子串数目。

样例

5
04321
6
解释#1

六个满足条件的串分别为 4, 432,32,0,04,0432。

样例#2

详见 选手文件夹 下的 string/string2.in/ans 文件。

数据范围

  • 对于 10% 的数据:满足 N = 1。
  • 对于 60% 的数据:满足 1 <= N <= 10^3。
  • 对于 100% 的数据:满足 1 <= N <= 10^6。

第一眼我们就看到了文件提示,这意味着我们的代码前面需要加上

//freopen(".in", "r", stdin);
//freopen(".out", "w", stdout);

当然,递交的时候注释需要去掉。

接下来是题目背景,一般情况下对题目影响不大,所以可以简单扫一眼,直接过掉。
然后就是问题描述,在看这个地方的时候一定不要放过每一句话,因为有可能你忽略的那句话就是题目的关键。
我们再把题目-问题描述拿过来看:


给定一个长为 N 的数字串(即由若干 0 ∼ 9 的数字构成的字符串),请你回答有多少连续子串(即从该串中选出连续的若干个数字,可以包括前导 0 是 4 或 5 的倍数。如果同时是 4 和 5 的倍数,应当只被计算一次)。


从中我们可以得知,输入数据里包含一个由若干数字组成的数字串,做了那么多题目的你们一定也想到:这题和字符串脱不了干系了。其次,我们要输出的是一个数(s),即为这个数字串里连续为4或5的倍数的可以有前导0的子串的个数
是不是很绕?简单总结一下子串的条件:

1.首先你要知道那些子串需要是连续的
每个子串:
2. 为4或5的倍数,可有前导0。

第二项怎么判断呢?
由于数据范围过大,我们肯定不能把子串转为int类型再做判断,这不仅麻烦还会超时。
你要知道,一个合格的编程者是需要掌握许多数学知识的。(如果一不小心刺痛了你向你道个歉~)
我们可以利用掌握的数学知识来解答:


4的倍数的数两位(/100)是4的倍数
5的倍数的数最后一位是05


这不就有思路了!
5的倍数很好判断,直接找0或5,再动s就好了(具体待会说)
4的话可以单独弄一个函数来判断后两位:

bool df(char x, char y) {
	if (int(x - '0') * 10 + int(y - '0') % 4 == 0) {
	    return 1;
	}
	return 0;
}

这不就OK了嘛。
至于如果同时是 4 和 5 的倍数,应当只被计算一次)。就用if-else好了
看吧,仔细琢磨琢磨题目,一些基本的思路不就出来了吗。
至于输入输出格式和样例都有解释,自己看好了,这里不做详解。
数据范围一定要看!很多思路都是搭建在数据上面的。

第二步:写代码

最激动人心的写代码部分来了。
经过上面的基本思路,我们可以大致确定代码的框架:

#include <bits/stdc++.h>
using namespace std;
long long n, s;
string a;
bool df(char x, char y) {
	if (((int(x - '0') * 10 + int(y - '0')) % 4) == 0) {
		return 1;
	}
	return 0;
}
int main() {
	//freopen("string.in", "r", stdin);
	//freopen("string.out", "w", stdout);
	cin >> n >> a;
	for (int i = 0; i < a.size(); i++) {
		//找子串
	}
	cout << s;
	return 0;
}

其实我们发现,思路有后,代码很简单的!
现在只有找子串部分使我们需要考虑的了。至于其他的,都考虑过了啊!

我们可以怎样写呢?
首先是if-else语句(避免重复)判断4、5倍数,由于5更简单,先写他吧:

if (a[i] == '0' || a[i] == '5') {
    s += (i + 1);
}

疑?为什么s是 += (i + 1)呢?
因为,只要末尾是这两个数,子串就是5的倍数。,并且排除了前导0的情况,直接加上他的下标不就不用重复算了嘛!至于+1,那是因为i是从0开始的。
else语句呢?

else {
    if (a[i] == '4' || a[i] == '8') {
        s++;
    }
    if (i != 0 && df(a[i - 1], a[i])) {
        s += i;
    }
}

第一个很好理解,因为个位4、8那他就可以单独成为一个“合格”的子串了。
第二个,也很好理解,毕竟i>0才能执行函数嘛!
那为什么第二个是s += i而不是s += (i + 1)呢?
那是因为你用的是两个if语句啊,+1在第一个已经过了
整体代码:

#include <bits/stdc++.h>
using namespace std;
long long n, s;
string a;
bool df(char x, char y) {
	if (((int(x - '0') * 10 + int(y - '0')) % 4) == 0) {
		return 1;
	}
	return 0;
}
int main() {
	//freopen("string.in", "r", stdin);
	//freopen("string.out", "w", stdout);
	cin >> n >> a;
	for (int i = 0; i < a.size(); i++) {
		if (a[i] == '0' || a[i] == '5') {
			s += (i + 1);
		} else {
			if (a[i] == '4' || a[i] == '8') {
				s++;
			}
			if (i != 0 && df(a[i - 1], a[i])) {
				s += i;
			}
		}
	}
	cout << s;
	return 0;
}

第三步:自测

先编译给的样例,没过就逐行找错、自运行、加log……
过了,最好自己出几个极端样例(因为普通样例没有意义)
完,递交,过了(没过就继续按照评测提示找错)。
这就差不多了吧。


第四步:总结

相对来说这题还是比较简单的。总的来说做一道题,首先是读题,非常重要。思路基本是从这来的。后期编写和修正也很重要,不过这里需要自己去尝试、实践,这样你才能收获更多。

最后,也祝大家能在编程之路上越走越稳,越走越远,越来越强大!

posted @ 2024-03-19 13:31  Befrepof  阅读(172)  评论(1编辑  收藏  举报