PTA题目:7-9 抢红包 (25分)

没有人没抢过红包吧…… 这里给出N个人之间互相发红包、抢红包的记录,请你统计一下他们抢红包的收获。

输入格式:

输入第一行给出一个正整数N(≤10​4​​),即参与发红包和抢红包的总人数,则这些人从1到N编号。随后N行,第i行给出编号为i的人发红包的记录,格式如下:

KN​1​​P​1​​⋯N​K​​P​K​​

其中K(0≤K≤20)是发出去的红包个数,N​i​​是抢到红包的人的编号,P​i​​(>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 );

 

很高兴,这下全弄明白了。

 

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值