没有人没抢过红包吧…… 这里给出N个人之间互相发红包、抢红包的记录,请你统计一下他们抢红包的收获。
输入格式:
输入第一行给出一个正整数N(≤104),即参与发红包和抢红包的总人数,则这些人从1到N编号。随后N行,第i行给出编号为i的人发红包的记录,格式如下:
KN1P1⋯NKPK
其中K(0≤K≤20)是发出去的红包个数,Ni是抢到红包的人的编号,Pi(>0)是其抢到的红包金额(以分为单位)。注意:对于同一个人发出的红包,每人最多只能抢1次,不能重复抢。
输出格式:
按照收入金额从高到低的递减顺序输出每个人的编号和收入金额(以元为单位,输出小数点后2位)。每个人的信息占一行,两数字间有1个空格。如果收入金额有并列,则按抢到红包的个数递减输出;如果还有并列,则按个人编号递增输出。
输入样例:
10
3 2 22 10 58 8 125
5 1 345 3 211 5 233 7 13 8 101
1 7 8800
2 1 1000 2 1000
2 4 250 10 320
6 5 11 9 22 8 33 7 44 10 55 4 2
1 3 8800
2 1 23 2 123
1 8 250
4 2 121 4 516 7 112 9 10
输出样例:
1 11.63
2 3.63
8 3.63
3 2.11
7 1.69
6 -1.67
9 -2.18
10 -3.26
5 -3.26
4 -12.32
分析:
这是和之前的德才论一样类型的题目,给出大量数据,然后按一定的规则排序。
具体考点:结构体,结构体指针,qsort排序,对于浮点数误差的理解。
大的框架我是完全能写出来的。
但细节上出了问题。
排序函数参数cmp
int cmp(const void *a,const void *b)
{
struct people *A = (struct people *)a;
struct people *B = (struct people *)b;
if(fabs(A->mon>B->mon)>=1e-15)
{
if(A->mon>B->mon) return -1;
else if(A->mon<B->mon) return 1;
}
// if(A->redc>B->redc) return -1;
// else if(A->redc < B->redc) return 1;
// if(A->id > B->id) return 1;
// else if(A->id < B->id) return -1;
}
起初知识两行指针赋值+三对排序。但结果和案例不同。开头三位是1,8,2。但答案是1,2,8。经反复的注释,控制变量思想,发现应该是第三重排序,也就是id来控制2在8的前面,但有没有这段代码都一样,也就是说明没有起作用。
思考认为,或许是double的误差,以前见到过,是说double不能简单地说==/!= , 因为他的有效位数一定,而之后位就会有波动。于是我怀疑,对于2和8的排序,或许根本没有进入第三重,而在第一重就因为所谓的误差走了return的路。
那么,不妨承认误差。这才有了上面的代码。
来源:https://blog.csdn.net/Rex_WUST/article/details/89219529
我便仿照这修改,如上。却发现之后的排序却变得一团糟。似乎这样写不行。
于是,这就想办法消除误差。发现题中最后输出的都是两位小数。我就想,或许实现对浮点数保留至2位在进行比较,不就行了吗。说动手就动手。
我学习了这位老师的思路:https://blog.csdn.net/weixin_42643216/article/details/100788103
便补充
for(i=1;i<=N;i++)
{
if(money[i].mon>0)
{
f = money[i].mon + 0.005;
temp = f*100;
f = 1.0*temp/100;
money[i].mon = f;
}
}
他确实起到了一些作用,但这个方法仅适用于正数,我的负数四舍五入,最后结果和理论输出偏了0.01.于是pta判断还是错误。
然后我偷鸡着改了改,上述操作只针对正数,然后就AC了。
附上代码:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
struct people{
int id;
double mon;
int redc;
}money[20000],t;
int cmp(const void *a,const void *b)
{
struct people *A = (struct people *)a;
struct people *B = (struct people *)b;
if(A->mon>B->mon) return -1;
else if(A->mon<B->mon) return 1;
if(A->redc>B->redc) return -1;
else if(A->redc < B->redc) return 1;
if(A->id > B->id) return 1;
else if(A->id < B->id) return -1;
}
int main()
{
int i,N,id,mon,n,j,temp;
double f;
scanf("%d",&N);
for(i=1;i<=N;i++)
{
money[i].mon = 0;
money[i].redc = 0;
money[i].id = i;
}
for(i=1;i<=N;i++)
{
scanf("%d",&n);
for(j=1;j<=n;j++)
{
scanf("%d %d",&id,&mon);
money[i].mon -= 1.0*mon/100;
money[id].mon += 1.0*mon/100;
money[id].redc++;
}
}
for(i=1;i<=N;i++)
{
if(money[i].mon>0)
{
f = money[i].mon + 0.005;
temp = f*100;
f = 1.0*temp/100;
money[i].mon = f;
}
}
qsort(money+1,N,sizeof(struct people),cmp);
for(i=1;i<=N;i++)
{
printf("%d %.2lf\n",money[i].id,money[i].mon);
}
return 0;
}
说来都是缘。就在昨天下午,看了看翁恺老师的C语言进阶课,关于浮点数的知识。
这又给了我新的想法。
上课笔记
浮点运算的精度
a = 1.234f 若只写1.234 ,则默认double,只有自己加上标注f/F,才为float。
案例
a = 1.234f
b = 1.123f
c = a+b;
if(c==2.357) printf----
实际上,判断发现c!=2.357.因为浮点数是有误差的。
可能真正的值是2.3579999321
【可见,在一些题目,或日常生活中,比u人银行存钱。就不能有浮点数表达。因为他会在暗中对误差进行累计。造成糟糕的结果。那么怎么处理呢?】
【可以先在计算时,把他们化成整型。比如算1.23元时,可以先写成123分】
情况已经很明朗了。我就陷入了浮点误差累计的深渊。
刚才按上面思路改了下。成功了,比原来也简单了。
改动:1.删除了浮点去位操作 2.结构体里的mon用int定义 3.每次累加时,直接加的分(话说题目本就给的分,是在暗示我?)
4.排序时也用的int的mon,只在最后输出时,才 printf("%d %.2lf\n",money[i].id, (double)money[i].mon / 100 );
很高兴,这下全弄明白了。