稀里糊涂WA之题目数据绝壁有问题

希望有一天这篇博客能被CSPCCF的出题专家看到。对于201612-2 工资计算这道题,我只能说,出题人的锅,最终评测数据都搞错,我觉得袁隆平老先生都会嘲讽一句:我怕是白让你们吃饱了。

牢骚发完了(必须发一下,因为简单的题占用了我一上午的时间,只因为出题人的粗枝大叶enmm)。

  • 题目如下:

  • 题目大意:

没错,就是这么一道学习C语言时练习Case语法时候的题目。

  • 做题思路:

题目给出税后所得T,让你求税前所得S。T的最大值是100000,第一反应是设置一个大数组a,数组的每一个下标代表一个税前所得,这样数组的值就代表税后所得,这样遍历每一个下标i即可得到所有的税后所得,那么再遍历一遍a比对输入即可。接下来就是数组的上界问题了,第一反应是让税收率为50%,那么数组的上界为20万,虽然有点大但这个上界还能接受。

然后,题目中说然而就是这句话(这句话和最终的评测数据冲突了,下文会详述,这就是为什么出题人白吃了这么多年的大白米饭的原因enmm)。

照着题目中的这句话,税前工资是一个整百的数,那么,数组的上界便可以变成原来的1/100,即2000,这样的数组大小简直完美。然后写出来了程序,交了一发WA了:

然后调试的过程中发现了很有趣的一个问题,这里以如下代码作为介绍:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int A = 6500;
    int T = A - 4500;
    int a = 10000 - 45 - 300 - (A - 4500) * 0.20;///9255 or 9254??
    int b = 10000 - 45 - 300 - T * 0.20;///9255 or 9254??
    int Y = T * 0.20;
    int c = 10000 - 45 - 300 - Y;///9255 or 9254??
    printf("a = %d\nb = %d\nc = %d\n", a, b, c);

}

如果是以前的我,我一定说输出的a=b=c=9255。但是程序执行结果确实让人大跌眼镜:

啪啪啪啪啪地打脸,这里涉及了int和double同时运算的知识,当时学习C语言的时候浅尝辄止了。到现在我还是模棱两可,希望有大神能够给出解释(本人QQ1497058369)。

然后我把数据类型都改成了double,又把数据运算改成了上述的第三种输出形式,程序代码如下(提交WA,但是这是出题人的锅,按理说应该是正确无疑的):

*#include<iostream>
#include<cstdio>
using namespace std;
double a[2000]={0};
int main(){

    double T;
    cin >> T;
    for (int i = 1; i <= 2000; i ++){
        if (i * 100 <= 3500)
            a[i] = i * 100;
        else{
            double A = i * 100 - 3500;
            double F_CK;
            if (0 < A && A <= 1500){
                F_CK = (A * 0.03);
                a[i] = i * 100 - F_CK;
            }
            if (1500 < A && A <= 4500){
                F_CK = (A - 1500) * 0.10;
                a[i] = i * 100 - 45 - F_CK;
            }
            if (4500 < A && A <= 9000){
                F_CK = (A - 4500) * 0.20;
                a[i] = i * 100 - 45 - 300 - F_CK;
            }
            if (9000 < A && A <= 35000){
                F_CK = (A - 9000) * 0.25;
                a[i] = i * 100 - 45 - 300 - 900 - F_CK;
            }
            if (35000 < A && A <= 55000){
                F_CK = (A - 35000) * 0.30;
                a[i] = i * 100 - 45 - 300 - 900 - 6500 - F_CK;
            }
            if (55000 < A && A <= 80000){
                F_CK = (A - 55000) * 0.35;
                a[i] = i * 100 - 45 - 300 - 900 - 6500 - 6000 - F_CK;
            }
            if (A > 80000){
                F_CK = (A - 80000) * 0.45;
                a[i] = i * 100 - 45 - 300 - 900 - 6500 - 6000 - 8750 - F_CK;
            }
        }
    }
    for (int i = 1; i <= 2000; i ++){
        if ((int)a[i] == (int)T){
            cout << i * 100 << endl;
            break;
        }
        ///printf("%d\n", (int)a[i]);
    }
    return 0;
}

然后WA了,头铁又改了一下程序再交,又多WA了几发enmm。

然后我就纳闷了,连一个数据都过不了?!!

去网上找了“大神们”的代码,这个大神为什么加引号呢?因为大神有时候并没有发现自己的错误,而是因为出题人的错误而AC了。

例如,下面是某位大神AC的代码【不敢苟同】,此人用的是分段函数,显然用int就不太正确,忽略了double和int运算:

明显,输入100000,运行结果就不对:

结果应该是154400,上述结果是154399,然后,我把代码贴到上面交了一发,竟然过了enmmm:

再说一遍laji出题人,辣鸡测试数据。

然后,从网上找到了一个真大神的代码,在此基础上写了一个cpp,用于检测我的代码的正确性,原理是把我的a数组都输出保存在testDate.txt中作为大神代码的输入,这样便可检测了。如下:

#include <bits/stdc++.h>
using namespace std;
//#define DEBUG
int salaryrange[] = {3500, 3500+1500, 3500+4500, 3500+9000, 3500+35000, 3500+55000, 3500+80000 };
int taxrate[] = {3, 10, 20, 25, 30, 35, 45};
const int SIZE = sizeof(salaryrange) / sizeof(int);
int range[SIZE];
int main()
{
    int t, s;
    // 计算各种收入范围
    range[0] = salaryrange[0];
    for(int i=1; i<SIZE; i++) {
        range[i] = range[i-1] + (salaryrange[i] - salaryrange[i-1])
                - (salaryrange[i] - salaryrange[i-1]) * taxrate[i-1] / 100;
    }
#ifdef DEBUG
    for(int i=0; i<SIZE; i++)
        cout << range[i] << " ";
    cout << endl;
#endif

    // 输入数据:
    fstream input("testData.txt");
    int ans = 1;
    while (!input.eof()){
        input >> t;
        // 计算收入范围
        int i;
        for(i=0; i<SIZE; i++)
            if(t <= range[i])
                break;
        // 计算税前工资
        if(i == 0)
            s = t;
        else {
            s = salaryrange[i-1] + (t - range[i-1]) * 100 / (100 - taxrate[i-1]);
        }
        // 输出结果
        //cout << s/100 << endl;
        if (s/100 != ans ++)
            cout << "********" << endl;
    }
    return 0;
}

运行如下:

显然,大神的结果和我的结果完全一样,输出一行星号是因为eof的缘故,可以忽略。

那么,为什么还是不对呢????

我把大神的代码再次运行,输入1,输出也是1,然而我的程序却不行。然后我把代码改回了原来的数组上界大小,如下【AC代码,但明显和题目中的这句话冲突】:

#include<iostream>
#include<cstdio>
using namespace std;
double a[154401]={0};
int main(){
    double T;
    cin >> T;
    for (int i = 1; i <= 154400; i ++){
        if (i <= 3500)
            a[i] = i;
        else{
            double A = i - 3500;
            double F_CK;
            if (0 < A && A <= 1500){
                F_CK = (A * 0.03);
                a[i] = i - F_CK;
            }
            if (1500 < A && A <= 4500){
                F_CK = (A - 1500) * 0.10;
                a[i] = i - 45 - F_CK;
            }
            if (4500 < A && A <= 9000){
                F_CK = (A - 4500) * 0.20;
                a[i] = i - 45 - 300 - F_CK;
            }
            if (9000 < A && A <= 35000){
                F_CK = (A - 9000) * 0.25;
                a[i] = i - 45 - 300 - 900 - F_CK;
            }
            if (35000 < A && A <= 55000){
                F_CK = (A - 35000) * 0.30;
                a[i] = i - 45 - 300 - 900 - 6500 - F_CK;
            }
            if (55000 < A && A <= 80000){
                F_CK = (A - 55000) * 0.35;
                a[i] = i - 45 - 300 - 900 - 6500 - 6000 - F_CK;
            }
            if (A > 80000){
                F_CK = (A - 80000) * 0.45;
                a[i] = i - 45 - 300 - 900 - 6500 - 6000 - 8750 - F_CK;
            }
        }
    }
    for (int i = 1; i <= 154400; i ++){
        if ((int)a[i] == (int)T){
            cout << i << endl;
            break;
        }
        ///printf("%d\n", (int)a[i]);
    }
    return 0;
}

然后,AC了

F*CK!F*CK!F*CK!

这说明最终的测试数据有不是整百的输入。

出题人,算你狠。

今天就不再不正经了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值