[BZOJ 3330] 分数 三分法+精度优化
来源:互联网 发布:linux tail 100f 编辑:程序博客网 时间:2024/06/02 19:08
题目传送门:【BZOJ 3330】
题目大意:出题最困难的地方在于调控分数,题目必须让选手之间的差异体现出来。而参加考试的选手的能力水平分布严重影响了题目的得分分布,适合一个省份的题目换到另一个省份可能就会变得没有意义。这里面有一个微妙的关系。为了更好地把握题目的难度,小强建立了一个模型,每个选手的实力都是一个 0 到 100 之间的实数。小强可以掌控一个题目的“难度”和“区分度”。一个选手的得分是:
其中
其中,
输入的第一行为两个正整数 N 和 P,表示选手个数以及精度要求。接下来的 N 行,每行包含一个整数表示选手实力,范围为 [ 0 , 100 ]。
输出为一个实数,表示偏差的最小值,取 P 位有效数字,向下取整。
题目分析:
这道题需要让你求出使偏差最小的难度和区分度的大小。根据题目下方的难度-区分度的图表,结合题意,可以发现偏差值与难度-区分度的关系为一个单峰函数。因此我们可以对其进行三分。
由于有两个变量(难度,区分度),所以我们先固定一个变量,对另一个变量进行三分操作。在这里,我们最好先固定难度,先对区分度进行三分,求出当前难度下区分度最优的情况下的偏差值,然后根据偏差值的大小再对难度进行三分(也就是三分套三分的意思)。直接使用此方法即可……
……
……
……
你以为就完了吗?
这么快就完了才怪呢! 以下为博主 BB 时间,如果你也被莫名其妙的 WA 给弄得心烦意乱,可以参考以下内容。(注意仅仅是参考!没遇到的请轻喷)
1.本题玄学卡精度,直接使用 double 很可能在 P = 10 时爆掉,需要使用 long double 才可以。
2.本题输出方式极为蹊跷,由于输出时需要向下取整,同时保留有效位数,所以为了方便需要先将 long double 转化为 long long,之后再一位一位地输出,同时还要特判小数点的位置。
3.特别注意,计算偏差时,当实际分数过小或者接近 100 时,一定要特判!!
4.本题难度和区分度的取值都非常小,所以三分上下界没必要设得太大,难度在 0 - 15 以内,区分度在 0 - 1 以内就可以了。
下面附上代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int MX = 23;const long double eps = 1e-9;int n,P;int strength[MX]; //实力值 long double diff_lf,diff_rt,diff_lm,diff_rm; //难度 long double lf,rt,lm,rm; //区分度 long double d; //公差,用于求理想分数 bool comp(const int& i,const int& j){ return i > j;}long long mypow(int a,int b){ //在浮点数转化为整数时使用 long long r = 1,base = a; while (b){ if (b & 1) r *= base; base *= base; b >>= 1; } return r;}inline long double sigma(long double diff,long double dist){ long double sum = 0,idel = 100; for (int i = 1;i <= n;i++){ long double score = 100 / (1 + exp(diff - dist * strength[i])); if (score < 1e-12){ sum += (100.0 - idel) * log(100 / (100 - score)); } else if (score >= 100){ sum += (idel * log(100 / score)); } else sum += (idel * log(100 / score) + (100.0 - idel) * log(100 / (100 - score))); idel -= d; } return sum;}void ans(long double val,int p){ long long w = 1; //判断小数点前有多少位 int ups = 0,used = 0; //控制小数点的位置及是否补 0 for (int i = 1;;i++){ if (val / w*1.0 < 1) break; w *= 10,ups++; } long long res = (long long)(val * mypow(10,10 - ups)),highest = 1000000000; for (int i = 9;i >= 10 - p;i--){ if (i == 9 - ups){ if (i == 9) printf("0"); printf("."); } cout<<(res / highest); res %= highest; used++; highest /= 10; } for (;used < ups;used++){ printf("0"); }}int main(){ cin>>n>>P; d = 100.0 / (n - 1) * 1.0; for (int i = 1;i <= n;i++) cin>>strength[i]; sort(strength + 1,strength + n + 1,comp); diff_lf = 0,diff_rt = 15; //设定难度的上下界 diff_lm = diff_lf + (diff_rt - diff_lf) / 3; diff_rm = diff_rt - (diff_rt - diff_lf) / 3; long double lf = 0,rt = 1,min_lm,min_rm,lm,rm; //设定区分度的上下界 while (diff_rt - diff_lf > eps){ diff_lm = diff_lf + (diff_rt - diff_lf) / 3; diff_rm = diff_rt - (diff_rt - diff_lf) / 3; lf = 0,rt = 1; //每次三分难度时,都要重置区分度 lm = lf + (rt - lf) / 3; rm = rt - (rt - lf) / 3; while (rt - lf > eps){ lm = lf + (rt - lf) / 3; rm = rt - (rt - lf) / 3; if (sigma(diff_lm,lm) < sigma(diff_lm,rm)) rt = rm; else lf = lm; } min_lm = sigma(diff_lm,lm); lf = 0,rt = 1, lm = lf + (rt - lf) / 3, rm = rt - (rt - lf) / 3; while (rt - lf > eps){ lm = lf + (rt - lf) / 3; rm = rt - (rt - lf) / 3; if (sigma(diff_rm,lm) < sigma(diff_rm,rm)) rt = rm; else lf = lm; } min_rm = sigma(diff_rm,lm); if (min_lm < min_rm) //根据两个三分点的答案不同来更新难度 diff_rt = diff_rm; else diff_lf = diff_lm; } ans(sigma(diff_lm,lm),P); return 0;}/*Some example inputs: 6 688 72 30 15 7 46 1088 86 82 74 70 685 10100 20 15 10 83 8100 50 04 880 60 40 204 10100 100 100 100*/
- [BZOJ 3330] 分数 三分法+精度优化
- 【三分法】【bzoj 3330】: [BeiJing2013]分数
- BZOJ 4868: [Shoi2017]期末考试 (三分法)
- 分数的精度
- 分数化小数(指定精度)
- BZOJ 1857 SCOI 2010 传送带 三分法
- HDU 3714 Error Curves (三分法注意判断精度)
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- Servlet学习
- 多态性和虚函数
- Linux文件的压缩与解压缩
- 模糊查询
- Windows下使用Sublime Text配置C++编译环境
- [BZOJ 3330] 分数 三分法+精度优化
- 1059. Prime Factors (25)
- 基于Docker及Kubernetes技术构建容器云(PaaS)平台概述
- 动态单链表库函数和demo
- 二维数组中的查找
- yii-advanced-app-2.0-归档文件安装
- 冒泡排序算法
- PopupWindow增加半透明蒙层
- 2017/7/23 学习心得 css3