问题 Q: 出线概率(10分)
[命题人 : 外部导入]
时间限制 : 1.000 sec 内存限制 : 128 MB
题目描述
2022世界杯正在火热进行中,小南是足球球迷,想知道某个球队的出线概率。经过严肃认真的研究,他发现其出线概率是一种可以计算的玄学:根据球队的名字可以得到出线概率!球队的名字由n(1≤n≤30)个小写字母c1, c2, …, cn组成,小写字母只包括’a’‘i’,其编号分别对应19,则出线概率的计算公式为:
(-1)1 (c1对应的编号) * 101 +(-1)2(c2对应的编号)102 + … (-1)n(cn对应的编号)*10n )/10n+1
最终结果保留到小数点后n位。例如当球队名字为fff时,f字母对应的编号是6,则出线概率为:(-60+600-6000)/10000=-0.546。
给定某个球队的名字,请你帮助小南计算一下该球队出现在下次世界杯的概率吧!注意有些球队的名字组成很长哦,最大可达30位呢。
输入
多个样例。每个样例输入1行包含一个由小写字母组成的字符串(小写字母只包括’a’~‘i’),表示球队的名字,长度为n(1≤n≤30)。
输出
每个样例输出一个实数表示出线概率,保留到小数点后n位。
样例输入 Copy
fff
aaaa
样例输出 Copy
思路
初看
这可太简单了,题说啥干啥。嘎嘎敲。‘‘保留到小数点后n位”,直接利用转换说明%nd补充n。成功答案错误。
#include<stdio.h>
#include<string.h>
int main()
{
char arr[31];
while (~scanf("%s", arr))
{
int n = 0;
n=strlen(arr);
int xiang = 1, gailv=0,jia=1;
for (int i = 0; i <= n - 1; i++)
{
xiang *= (-1) * 10;
jia = xiang*(arr[i] - 'a' + 1);
gailv += jia;
}
printf("%.nd\n", gailv);//不能这样搞转换说明。
//gailv忘记除最后一项10的n减1次方。处处是bug。。。。
}
return 0;
}
再看
浮点数精度是会丢失的。那么既然如此,先略去要除去10的n减1次方,搞出整数。先输出-0.或者0.,然后计算出需要补足的0。将计算出前面
(-1)1 (c1对应的编号) * 101 +(-1)2(c2对应的编号)102 + … (-1)n(cn对应的编号)*10n )这一串。负数用强制类型转换为正数。
int wei(int b)
{
int bb = b; int n = 0;
while (bb)
{
bb /= 10;
n++;
}
return n;
}
#include<stdio.h>
#include<string.h>
int main()
{
char arr[31];
while (~scanf("%s", arr))
{
int n = 0;
n = strlen(arr);
int xiang = 1, gailv = 0, jia = 1;
for (int i = 0; i <= n - 1; i++)
{
xiang *= (-1) * 10;
jia = xiang * (arr[i] - 'a' + 1);
gailv += jia;
}
while (gailv % 10 == 0)
{
gailv /= 10;
}
if (gailv < 0)
{
printf("-0.");
for (int i = 1; i <= n - wei(gailv); i++)
printf("0");
printf("%u\n", (unsigned)gailv);//强转失败。
//谁教的这样想当然的转。。。。。。
}
else
{
printf("0.");
for (int i = 1; i <= n - wei(gailv); i++)
printf("0");
printf("%d\n", gailv);
}
}
return 0;
}
为啥不对,因为对强制转换概念不清楚。-3转换为unsigned难道是3吗。当然不是。-3在计算机中的补码是4294967293的二进制形式。即为2的三十二次方4294967296(十进制)减去3等于4294967293。以无符号整型读写当然不对咯。
#include<stdio.h>
int main(void)
{
int a = -2;
int b = (unsigned)a;//b=-2;
unsigned m= (unsigned)a; //m=4294967294
printf("%u\n", -3);//4294967293
printf("%o", -3);//37777777775(八进制)=十进制的4294967293
return 0;
}
再再看
那么当是负数的时候,直接加负号把负数变成正数。这下题目中的样例对了。但是oj依旧不通过,注意有些球队的名字组成很长哦,最大可达30位呢。 定睛一看,原来。。。。。这用long long都不行。何况是int。。。。折腾这么久前面全是错误的轨道。看对题目很重要。只能数组出马了一个个输出数字。
终版
(-1)1 (c1对应的编号) * 101 +(-1)2(c2对应的编号)102 + … (-1)n(cn对应的编号)*10n )/10n+1化简分式上下全部同除10;
根据题意,要填满数组的n位。如何填满呢。先观察题目,由于序号是1到9,那么(-1)1 (c1对应的编号) * 100 这一项一定比后一项(-1)2(c2对应的编号)*101 要小,(-1)1 (c1对应的编号) * 100 +(-1)2(c2对应的编号)*101 )一定是一个正数,我们利用加法结合律,先加第一项和第二项,第三项和第四项,第五项和第六项等。那么正好对应的每两项和的前两位都对应要求数组的两位(由于1到9,所以不会产生进位的问题,正好对应)。
如果这个n是奇数呢。前面的都两两配对,最后剩下的第n位加上前面的所有位,岂不是会把数组扰乱。那么我们进行人为的运算。例如aaa,-1+10-100=9-100=-(100-9)=-91,那么最后一位就是10减9等于1,前一位9减0等于9。由于第一项和第二项不可能是0,第一项加第二项末尾一定是有数字的,而不是0,所以第一项加第二项,第三项加第四项等等和的末尾一位一定是有数字的,而第n位的末尾是0,所以一定会借一位。而第n位一定是1或2或3或4或5等后面加上n减1个零,被借位后引起一系列推塔式反应。最后一位10减原来对应的数字,而前面的数字都要用9减去,最高位减1.
include<stdio.h>
#include<string.h>
int main()
{
char arr[31]; int shuchu[31] = { 0 };
while (~scanf("%s", arr))
{
int n = 0;
n = strlen(arr);
int xiang = 1, gailv = 0, jia = 1;
for (int i = 1; i <= n-1 ; i+=2)
{
shuchu[n-i] = ((arr[i] - 'a' + 1) * 10 - (arr[i-1] - 'a' + 1))%10;
shuchu[n-i - 1] = ((arr[i] - 'a' + 1) * 10 - (arr[i-1] - 'a' + 1)) /10;
}
if (n % 2 == 1)
{
被借位后引起一系列推塔式反应。最后一位10减原来对应的数字,而前面的数字都要用9减去,最高位减1.
shuchu[0] = (arr[n-1] - 'a' +1-1);//最高位减1
printf("-0.%d",shuchu[0]);
for (int i = 1; i < n - 1; i++)
{
printf("%d", 9-shuchu[i]);//而前面的数字都要用9减去.
}
printf("%d", 10 - shuchu[n - 1]);//最后一位10减原来对应的数字
}
else
{
printf("0.");
for (int i = 0; i <= n - 1; i++)
{
printf("%d", shuchu[i]);
}
}
printf("\n");
}
return 0;
}
总结
当遇到数字特别长的时候要警惕,要用数组或者字符串等解决问题。