希望有一天这篇博客能被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!
这说明最终的测试数据有不是整百的输入。
出题人,算你狠。
今天就不再不正经了。