前言
作为一名初涉算法的初学者,学业不精,思路粗糙,万望指点、优化以及相互交流沟通学习
乒乓球问题的题目与C++解
题目详情
题目描述:
国际乒联现在主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。
其中11分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。
华华就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白11分制和21分制对选手的不同影响。
在开展他的研究之前,他首先需要对他多年比赛的统计数据进行一些分析,所以需要你的帮忙。
华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在11分制和21分制下,双方的比赛结果(截至记录末尾)。
比如现在有这么一份记录,(其中W表示华华获得一分,L表示华华对手获得一分):
WWWWWWWWWWWWWWWWWWWWWWLW
在11分制下,此时比赛的结果是华华第一局11比0获胜,第二局11比0获胜,正在进行第三局,当前比分1比1。
而在21分制下,此时比赛结果是华华第一局21比0获胜,正在进行第二局,比分2比1。
如果一局比赛刚开始,则此时比分为0比0。
你的程序就是要对于一系列比赛信息的输入(WL形式),输出正确的结果。
输入格式:
每个输入文件包含若干行字符串(每行至多20个字母),字符串由大写的W、L和E组成。
其中E表示比赛信息结束,程序应该忽略E之后的所有内容。
输出格式:
输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。
其中第一部分是11分制下的结果,第二部分是21分制下的结果,两部分之间由一个空行分隔。
输入样例:
WWWWWWWWWWWWWWWWWWWW
WWLWE
输出样例:
11:0
11:0
1:1
21:0
2:1
说明:
在一场乒乓球比赛中,当且仅当一人分值高于11/21分且较另外一人高至少两分,结束比赛,即判胜。
题目解读
难点:
在输入格式中,我们特别的关注到:每行至多20个字母,也就是说,会有额外的换行符‘\n’输入。我们似乎无法解决这个问题,我们既不能使用二维数组通过for循环输入解决(未知输入几行),也似乎不能使用cin,scanf()这些会跳过空格和换行符的输入方式,于是,我们想到了使用getchar()进行一个伪数据判断。
算法描述
定义一个较长的字符数组,使用getchar()及伪数据判断接受全部的输入。
利用for循环遍历这个字符数组,遇到’W’华华得一分,遇到’L’对手得一分。
根据题意进行格式控制–当一局结束后(一次判胜)输出一次结果,当比赛结束(数组遍历结束)输出当前场次的结果。
代码实现
#include<iostream>
using namespace std;
char a[1000000];
int main()
{
int i = 0;
a[i] = getchar();//伪数据判断输入,利用getchar()不会因为换行和空格结束输出的特点,选择'E'
//作为输入结束的判定
while (a[i] != 'E')
{
i++;
a[i] = getchar();
}
int huahua = 0, duishou = 0;
for (int j = 0; j < i; j++)//i记录的是不包括'E'的长度,E输入后并没有进行i++
{
if (a[j] == 'W')huahua++;
else if(a[j]=='L')duishou++;
//如果有判胜负则输出
while (huahua >= 11 && huahua - duishou >= 2 || duishou >= 11 && duishou - huahua >= 2)
{
printf("%d:%d\n", huahua, duishou);
huahua = 0, duishou = 0;
}
}
//输出遍历结束后两人当前赛局并控制格式
printf("%d:%d\n", huahua, duishou);
printf("\n");
huahua = 0, duishou = 0;
for (int j = 0; j < i; j++)
{
if (a[j] == 'W')huahua++;
else if(a[j]=='L')duishou++;
while (huahua >= 21 && huahua - duishou >= 2 || duishou >= 21 && duishou - huahua >= 2)
{
printf("%d:%d\n", huahua, duishou);
huahua = 0, duishou = 0;
}
}
printf("%d:%d", huahua, duishou);
return 0;
}
补充:伪数据判断/输入值判断
使用场景:
未知将要输入多少个数据,但是知道输入结束的标志符。注意,如果空格和换行都算作输入结束时,用一般的cin或者scanf()基本上都可以完成这个任务,这里的输入结束符应该是一个比较特殊的情况,如需要输入空格,以回车结束,或者是以题目给定的,例如本题’E’这种符号。
代码实现:
char a[100000],int i=0;
a[i] = getchar();//利用输入值来判断是否停止输入,所以要先进行一次输入
while (a[i] != 'E')
{
i++;
a[i] = getchar();
}
高精度加法的C++解决办法
题目详情
题目描述:
对于输入的两个不超过100位数的非负整数,给出两数之和。
输入格式:
在两行中分别给出两个不超过100位数字的非负整数。
输出格式:
在一行中输出两数之和。
输入样例:
123
12
输出样例
135
题目解读:
难点:
相比于常规的A+Bproblem,此题非常规的点是这两个给定的A,B是不超过100位数字的非负整数,两个数据大小都超过了C及C++中的所有数据类型,作为不具备高精度加法的编程语言,我们就无法简单以A+B去解决这个问题。
解题思路:
利用小学二年级知识,我们可以逐位逐位相加,满十进一。由此,解题思路应运而生。
算法描述
- 利用两个字符数组接受输入的数字字符。
- 将字符串数组转换为整型数组并倒转数组。
- 逐位相加,并使用一个数组存储各位相加的数字。遵循满十进一规则。
- 输出结果。
代码实现
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char A[100] = { '\0' }, B[100] = { '\0' };
cin >> A;
cin >> B;
//分别计算字符数组A,B的长度a,b并记录一个较大值c
int a1 = strlen(A);
int b1 = strlen(B);
int c1 = a1 >= b1 ? a1 : b1;
int a[100] = { 0 }, b[100] = { 0 };
for (int i = 0; i < a1; i++)
{
a[i] = A[a1 - 1 - i] - 48;//实现字符向数字转换 并倒转整个数组
}
for (int i = 0; i < b1; i++)
{
b[i] = B[b1 - 1 - i] - 48;
}
int sum[101] = { 0 };//为什么sum的数组要多开一个长度呢?
for (int i = 0; i < 100; i++)
{
int item = a[i] + b[i];
sum[i] += item % 10;//对十取余,正常获取每位数字(即便它不需要进位)
sum[i + 1] = item / 10;//若满十则进一
}
if (sum[c1] != 0)//当在最高位进一时,若sum定义仍为一百的长度,可能会造成溢出,无法存储进位。
{
cout << sum[c1];
}
for (int i = c1 - 1; i >= 0; i--)//从高位到低位依序输出
{
cout << sum[i];
}
return 0;
}
问题:为什么统计sum时一个使用+=一个使用=?
for (int i = 0; i < 100; i++)
{
int item = a[i] + b[i];
sum[i] += item % 10;//对十取余,正常获取每位数字(即便它不需要进位)
sum[i + 1] = item / 10;//若满十则进一
}
因为,进位时,下一位永远是0(数组已全部初始化置0),我们可以简单地给它赋值为1,实现的操作与+=1相同;
然而,在计算当前位数时,有可能在计算上一位数时完成了进位操作,直接赋值就将上一次的进位1抵消掉了。
原题链接
https://www.luogu.com.cn/problem/P1601
高精度求累加和的问题与解
题目详情
题目描述:
使用求和公式求1到N的累加和大家都会,但是如果把N值变大呢,比如100位的整数,那该怎么求?
输入格式:
输入在一行中给出1个位数不超过100位的整数N。
输出格式:
对每一组输入,在一行中输出1+2+3+……+N的值。
输入样例:
在这里给出一组输入。例如:
10
输出样例:
在这里给出相应的输出。例如:
55
解题思路:
鉴于数字过大,简单的使用循环进行前N项和的求解显然是不可解的,因此,很容易想到使用高斯求和公式对简单的1+2+3+…+n问题求解(即等差数列前N项和公式)。
涉及相关知识:
高精度乘法、高精度数除低精度数。
代码实现:
#include<iostream>
#include<cstring>
using namespace std;
char A[101] = { '\0' };
int main()
{
cin >> A;
int along = strlen(A);
int a[100] = { 0 }, b[101] = { 0 },sum[220] = { 0 };
//字符型向整型数组转化,并倒置数组
for (int i = 0; i < along; i++) a[i] = A[along - 1 - i] - 48;
for (int i = 0; i < along; i++) b[i] = A[along - 1 - i] - 48;
//执行首项+末项
b[0]++;
//判断是否发生进位
for (int i = 0; i < along; i++)
{
b[i + 1] += b[i] / 10;
b[i] = b[i] % 10;
}
//计算(首项+末项)*项数
//这里只能确定a数组的长度为along b数组的长度是along还是along+1不确定
//虽然可以通过编写代码确认具体长度,但是,鉴于已经初始化整个数组为0
//我们无妨让最终结果的某一位再加上一个0
for (int i = 0; i < along; i++)
for (int j = 0; j < along + 1; j++)
sum[i + j] += a[i] * b[j];
//判断sum的进位情况
for (int i = 0; i < 220; i++)
{
int tmp = sum[i];
sum[i] = tmp % 10;
sum[i + 1] += tmp / 10;
}
//记录sum的有效长度(去掉前导0)同时,在本题背景中,无需担心最后结果是0。
int sumlen = 220;
while (sum[sumlen - 1] == 0)sumlen--;
//进行除2操作(高精度数除低精度数)
for (int i = sumlen - 1; i >= 0; i--)
{
int temp = sum[i];
sum[i] = temp / 2;
//需要注意:如果是高位余数,我们应该扩大十倍补至下一位(即落位)
if (i > 0)
{
sum[i - 1] += 10 * (temp % 2);
}
//但是,如果余数本身就是个位产生的,我们要记得把余数原原本本地还给个位哟
else if (i == 0)
{
sum[i] = temp / 2;
sum[i] += temp % 2;
}
}
//可惜,当这样做完除法后,我们又无法确定最后的sum有多少位了
//所以,依然要考虑去除前导0的情况
int flag = 0;//利用flag记录是否有过至少一次输出
for (int i = sumlen - 1; i >= 0; i--)
{
if (sum[i] == 0 && flag == 0);
else
{
flag = 1;
printf("%d", sum[i]);
}
}
return 0;
}
后记:
在最后输出前导0时,考虑到最后结果不会为0,可以更加优化这个去除前导0的步骤
int flag = 0;
for (int i = sumlen - 1; i >= 0; i--)
{
if (sum[i] != 0 || flag != 0)
{
printf("%d", sum[i]);
flag = 1;
}
}
return 0;