[2020牛客暑期多校训练营第五场] F.DPS 思维模拟

题目链接: F.DPS

又是一个雷声大雨点小的题,看来题不可貌相啊。一看输出又双叒叕以为是一个难题,没想到是第二个签到题,但这个签到签到的并不怎么成功。

题意

题目大意是输入n个数,然后按照要求输出相应的图案。
题目给了一个公式: s i = 50 d i m a x i d i {s_i= \lceil 50\frac{d_i}{max_id_i}\rceil } ,大意就是这个数除以n个数里最大的数然后乘上50向上取整。
得到的这个 s i {s_i} 为下列 ”-“ 的个数。
例如:
777
如果遇到 s i {s_i} 最大时,需要将中间最后一个空格替换为 ’ * '。

题解

这个题本来正常模拟就行,但我们偏偏栽在了精度问题上。
最常规的求 s i {s_i}

int t=(ll)d[i]*50;
if(t%maxx==0) t=t/maxx+1;
else t/=maxx;

还有

int t=ceil(d[i]*50.0/maxx);
但注意!!!

t = c e i l ( d [ i ] 1.0 / m a x x 50.0 ) ; { \cancel{t=ceil(d[i]*1.0/maxx*50.0);}}
本来以为先除后乘能避免爆long long,但谁知道在这样会损失精度。
我写了一个测试代码

int main()
{
	int maxx;
	cin >> maxx;
	for(int i=1;i<=maxx;i++)
	{
		int r1=ceil(i*50.0/maxx);
		int r2=ceil(i*1.0/maxx*50.0);
		if(r1!=r2)
		{
			cout << "find!" << endl;
			cout << i << endl;
			printf("%d %d\n",r1,r2);
		}
	}
}

以d[i]最大范围值43962200为例,发现的确存在精度损失。
输出结果
以i为6154708为例,

int main()
{
	int maxx=43962200,i;
	i=6154708;
	int r1=ceil(i*50.0/maxx);
	int r2=ceil(i*1.0/maxx*50.0);
	printf("%.15lf\n",(i*1.0)/maxx);
	printf("%.15lf\n",(i*1.0)/maxx*50);
}

结果:
在这里插入图片描述

发现0.14*50出现精度错误!!

(i*0.1)后会转换为double类型,而double类型有效数字16位,16位以后可能会出现精度偏差,所以会出现结果7.000000000000001!=7的情况,进而导致向上取整出现错误。
所以综上所述,不能先除后乘。

代码

ll d[205];
int main()
{
    int n;
    cin >> n;
    ll maxx=-1;
    for(int i=0;i<n;i++)
    {
        cin >> d[i];
        maxx=max(maxx,d[i]);
    }
    for(int i=0;i<n;i++)
    {
        int r=ceil((d[i]*50.0)/maxx);
        cout << "+";
        for(int j=0;j<r;j++) cout << "-";
        cout << "+" << endl;
        cout << "|";
        if(d[i]==maxx)
        {
            for(int j=0;j<r-1;j++) cout << " " ;
            cout << "*|" << d[i] << endl;
        }
        else {
            for(int j=0;j<r;j++) cout << " " ;
            cout << "|" << d[i] << endl;
        }
        cout << "+";
        for(int j=0;j<r;j++) cout << "-";
        cout << "+" << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/107645032