其实本题的难度真心不高,但是可以完整说明数据分析、标程、随机数生成、对拍等部分。
题目链接
原题来自计蒜客的某次比赛。计蒜客对应的链接为https://nanti.jisuanke.com/t/42227。
或者我自己OJ的链接为http://47.110.135.197/problem.php?id=5150。
题面
给定一个正整数 n,请找出一个不大于 n 的正整数 p,使得 n 除以 p 的余数最大,并求出这个最大的余数。
输入
只有一行,包含一个正整数 n。
输出
只有一行,包含你的答案。
样例输入
5
样例输出
2
数据范围
一共 20 个测试数据
对于前 30% 的数据,1 ≤ n ≤10。
对于前 60% 的数据,1 ≤ n ≤10^6。
对于前 90% 的数据,1 ≤ n ≤ 10^18。
对于前 100% 的数据,1 ≤ n ≤ 10^1000。
题目分析
解题思路
求余数,而且要求余数最大。哪么必然意味着除数要最小,哪么除数必然为 1。也就是说,这题就是一个 2 的余数问题。进一步分析,我们可以知道如果被除数为奇数,哪么这个最大的余数为 n/2;如果被除数为偶数,哪么这个最大的余数为 n/2 - 1。等效于(n-1)/2。
数据范围分析
30%的数据,1 ≤ n ≤10。可以用int可以表示 n。
60% 的数据,1 ≤ n ≤10^6。也可以用int可以表示 n。
90% 的数据,1 ≤ n ≤ 10^18。只能用unsigned long long来表示 n。
100% 的数据,1 ≤ n ≤ 10^1000。WTF,已经超过了unsigned long long的范围了,需要用到高精度表示。
代码
90%通过代码
没有学过高精度怎么办?只能拿自己可以拿到的分数,也就是使用unsigned long long可以拿到90%的分数。对应代码如下:
#include <iostream>
int main() {
unsigned long long n;
std::cin >> n;
std::cout << (n-1)/2 << std::endl;
return 0;
}
100%通过代码
如果要拿到满分必须使用高精度的除法。对应代码如下:
//5150
//http://47.110.135.197/problem.php?id=5150
#include <cstdio>
#include <cstring>
const int MAXN = 2000;
char str[MAXN];
int data[MAXN] = {};
int minus[MAXN] = {1};
int main() {
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
//读入数据
scanf("%s", str);
//反过来
int len = strlen(str);
int i;
for (i=0; i<len; i++) {
data[i] = str[len-i-1] - '0';
}
//减去1
for (i=0; i<len; i++) {
if (data[i] >= minus[i]) {
data[i] -= minus[i];
break;
} else {
data[i+1]--;
data[i] = 10+data[i]-minus[i];
}
}
//除以2
for (i=len-1; i>=0; i--) {
if (1==data[i]) {
data[i-1] += data[i]*10;
data[i] = 0;
} else if (1==data[i]%2) {
//有余数
data[i] /= 2;
if (i>0) {
data[i-1] += 10;
}
} else {
data[i] /= 2;
}
}
//删除后导零
for (i=len-1; i>=0; i--) {
if (0!=data[i]) {
break;
}
len--;
}
//输出
for (i=len-1; i>=0; i--) {
printf("%d", data[i]);
}
if (0 == len) {
printf("0");
}
printf("\n");
//fclose(stdin);
//fclose(stdout);
return 0;
}
对拍
怎么验证自己写的高精度代码是正确的,很简单对拍啊。说实话,我自己也是通过对拍修改了好几次代码才通过的。
我们使用90%通过代码作为标准程序和高精度代码进行对拍。虽然只能有90%的数据集通过测试,这样对拍可以保证高精度代码也是正确的。因此我们首先需要写一个随机数据生成cpp。
随机数据生成
由于标准程序最大的数据范围是 unsigned long long,也就是18446744073709551615,对应为1e19。所以要注意产生的数据不能操作1.845e19这个大小。下面是参考的随机数生成代码。
//生成数据 5150
#include <cstdio>
#include <cstdlib>
#include <ctime>
int main(int argc, char *argv[]) {
int seed = time(NULL);
if (argc>1) {
//有参数
seed = atoi(argv[1]);
}
srand(seed);
//生成长度
const int lenMax = 19;//1000位
const int lenMin = 1;
int len = lenMin + rand()%(lenMax-lenMin+1);
int i;
for (i=0; i<len; i++) {
//输出0到9
int lo = 0;
int hi = 9;
int data = lo + rand()%(hi-lo+1);
if (0==i && 0==data) {
data = 1 + rand()%(hi);
}
printf("%d", data);
}
printf("\n");
return 0;
}
Win下对拍的BAT文件
@echo off
set cnt = 1
:loop
echo ==第%cnt%次测试
set /a cnt = %cnt% + 1
gen.exe %random% > data.in
std.exe < data.in >std.out
5150.exe < data.in > my.out
fc my.out std.out
if not errorlevel 1 goto loop
pause
goto loop
对拍的过程
生成了对应的gen.exe、std.exe和5150.exe后,我们执行对应的bat文件即可。下面是对拍的结果截图。
已经对拍了5677次,标准程序和高精度程序的结果都是一样的。
来源:CSDN
作者:justidle
链接:https://blog.csdn.net/justidle/article/details/103580625