派-详解-noi.openjudge.cn-二分答案
2016-10-11 20:19
435 查看
派-网址:http://noi.openjudge.cn/ch0111/05/
总时间限制: 1000ms 内存限制: 65536kB
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。
输入第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
样例输出
一、题目分析
最终分割的是体积
假如体积数据为 1 1 90 分3份
显然最优为 30 30 30
首先本身就有3份数据,所有第3大的1 可以肯定分下去 每个人分 1 1 1
然后对数据排序 第2大 + 第1大/2+第1大/2 可以是一种分法 2 45 45 ->2 2 2
第1大/3 + 第1大/3 + 第1大/3 可以是 30 30 30
那假如数据为 1 34 90 呢
分法1:1 1 1
分法2: 34 45 45-> 34 34 34
分法3:30 30 30->30 30 30
那如果是 1 70 90 呢
分法1: 1 1 1
分法2: 70 45 45 -> 45 45 45
分法3:30 30 30
是不是意味着:
如果n份派分成k份的所有情况是:在n>=k情况下
分法1:第k大 第k大 第。。。k大 共k个
分法2:第k-1大 第k-2大 ... 第3大 第1大/2 第2大/2
分法i: 第k-(i-1)大 第k-(i-2)大。。。第i+1大 紧着i个: 第1大/i 第1大/i 第1大/i
???????
对于 1 1 90 90
分3份:1 1 1 1----1 90 45 45-----90 30 30 30----23 23 23 23
但是还有一种 45 45 45 45 显然不只第1大可以分割 第2大也可以
也就是每次分割最大值情况:
1 1 90 45 45 重排序后:1 1 45 45 90
再分割:1 1 45 45 45 45
在分割:1 1 22.5 22.5 45 45 45
但没有这种分割更优: 1 1 90 30 30 30
再分割 1 1 30 30 30 45 45和1 1 22.5 22.5 45 45 45比起来,再取4份的时候更优
那分6份呢 显然 1 1 30 30 30 30 30 30 最优
---------------------------------------------
按照如上策略,贪心找分割规律解法失败,尝试去枚举结果,代入后找满足条件的值,
结果保留3位小数,可以很明确找到最大值如51.2364565,那么结果肯定在
0.000 0.001 0.002 。。。51.236 里面出现步长0.001
干脆给体积都乘以1000
0 1 2 ... 51236 找其中满足均分派的结果 比如
样例
28.2743334 28.2743334 50.2654816处理成
28274 28274 50265
那么结果肯定在 0-50265中间
比如 每个人拿到体积4 28274/4 + 28274/4 + 50265/4 > 3+1 也就是说人均4 肯定可以被分出来,然后遍历找其中最大的
但是2重循环下来题目时间复杂度为 O(n2*1000) 10^4*10^4*10^3 = 10^11次方超时
显然可以二分答案了,但是这个思路的第一个问题来了见如下代码
---------------------------------------------
只能过2个点
后来分析是在 left=25132 right=25134 mid=25133 并不符合切割规律因为去掉了小数点后的位数,25133是四舍五入来的
改进:都乘以10000 放大到小数点后4位
----------------------
只能过3个点
后面无论如何改都不行了,后来经看别人代码发现都是用的printf("%.3f")输出,
测试如下代码:
printf("%.1f", 3.45);
printf("%.1f", 3.445);
printf("%.1f", 3.5);
printf("%.1f", 3.55);
printf("%.3f", 3.44445);
printf("%.3f", 3.44455);
printf("%.3f",
3.44545);
printf("%.3f",
3.4454);
printf("%.3f",
3.4455);
我对c的格式化输出,程序的四舍五入彻底没了一个正确的推论,最后抛弃*10000这种放大策略,对实数
老老实实按照实数计算
-----------------------------------
AC代码
总结:1、其实这也是在解空间里面深搜枚举,只不过恰好有个符合特征的二分答案策略
2、在求解方程式 1+x=2+5 我们可以从上往下:x=2+5-1=6
也可以知道x为整数[0,10000]里面遍历枚举看哪个满足 if (x+1==2+5) {cout<<x;return;}
这正是枚举搜索,计算机傻瓜式工作的强项
3、本题坑点:有人说关于pi=3.1415926 精度不够会错5个点 所以pi=3.141592653589793 或者写出pi=cos(-1){math.h好像}
格式化输出,四舍五入,对于这种实数题目就不要渴望 while(left<right)中间差值为1,实数的比较本来就是一个精度为题
计算机里面的 0.0000000000000009 其实也是0 ,
While(right-left>1e-5) 基本就对了 如果3.14153基本等于3.14152
有人说while(right-left>1e-5)写成while(right-left>1e-8) 会超时,没测试
无论怎么着,感觉,思路简单,细节不少
---------------------------------------------------------------
其中多有不当之处,请批评指正
总时间限制: 1000ms 内存限制: 65536kB
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。
输入第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
3 3 4 3 3
样例输出
25.133
一、题目分析
最终分割的是体积
假如体积数据为 1 1 90 分3份
显然最优为 30 30 30
首先本身就有3份数据,所有第3大的1 可以肯定分下去 每个人分 1 1 1
然后对数据排序 第2大 + 第1大/2+第1大/2 可以是一种分法 2 45 45 ->2 2 2
第1大/3 + 第1大/3 + 第1大/3 可以是 30 30 30
那假如数据为 1 34 90 呢
分法1:1 1 1
分法2: 34 45 45-> 34 34 34
分法3:30 30 30->30 30 30
那如果是 1 70 90 呢
分法1: 1 1 1
分法2: 70 45 45 -> 45 45 45
分法3:30 30 30
是不是意味着:
如果n份派分成k份的所有情况是:在n>=k情况下
分法1:第k大 第k大 第。。。k大 共k个
分法2:第k-1大 第k-2大 ... 第3大 第1大/2 第2大/2
分法i: 第k-(i-1)大 第k-(i-2)大。。。第i+1大 紧着i个: 第1大/i 第1大/i 第1大/i
???????
对于 1 1 90 90
分3份:1 1 1 1----1 90 45 45-----90 30 30 30----23 23 23 23
但是还有一种 45 45 45 45 显然不只第1大可以分割 第2大也可以
也就是每次分割最大值情况:
1 1 90 45 45 重排序后:1 1 45 45 90
再分割:1 1 45 45 45 45
在分割:1 1 22.5 22.5 45 45 45
但没有这种分割更优: 1 1 90 30 30 30
再分割 1 1 30 30 30 45 45和1 1 22.5 22.5 45 45 45比起来,再取4份的时候更优
那分6份呢 显然 1 1 30 30 30 30 30 30 最优
---------------------------------------------
按照如上策略,贪心找分割规律解法失败,尝试去枚举结果,代入后找满足条件的值,
结果保留3位小数,可以很明确找到最大值如51.2364565,那么结果肯定在
0.000 0.001 0.002 。。。51.236 里面出现步长0.001
干脆给体积都乘以1000
0 1 2 ... 51236 找其中满足均分派的结果 比如
样例
28.2743334 28.2743334 50.2654816处理成
28274 28274 50265
那么结果肯定在 0-50265中间
比如 每个人拿到体积4 28274/4 + 28274/4 + 50265/4 > 3+1 也就是说人均4 肯定可以被分出来,然后遍历找其中最大的
但是2重循环下来题目时间复杂度为 O(n2*1000) 10^4*10^4*10^3 = 10^11次方超时
显然可以二分答案了,但是这个思路的第一个问题来了见如下代码
---------------------------------------------
只能过2个点
//#include <StdAfx.h> #include <stdlib.h> #include <iostream> using namespace std; #define PI 3.141592653589793 int n,f; unsigned int tj[10001],a[10001]; int IsOK(int res) { int i,sum=0; for (i=1;i<=n;i++) { sum += tj[i] / res; } if (sum>=f) return 1; else return 0; } int main() { int i,max=0,left,right; int ans; //读取数据 cin>>n>>f; f++; for (i=1; i<=n; i++) { cin>>a[i]; tj[i] = a[i]*a[i]*1000*PI; if (tj[i]>max) max=tj[i]; } //二分遍历查找 left=0; right=max; while(left<right) { int mid = (left+right)/2; if (IsOK(mid) ) ans=mid,left=mid+1; else right=mid-1; } if (IsOK(left)) ans=left; cout<<double(left/1000.0); //cin>>i; }
后来分析是在 left=25132 right=25134 mid=25133 并不符合切割规律因为去掉了小数点后的位数,25133是四舍五入来的
改进:都乘以10000 放大到小数点后4位
----------------------
只能过3个点
//#include <StdAfx.h> #include <stdlib.h> #include <iostream> using namespace std; #define PI 3.141592653589793 int n,f; unsigned int tj[10001],a[10001]; int IsOK(int res) { int i,sum=0; for (i=1;i<=n;i++) { sum += tj[i] / res; } if (sum>=f) return 1; else return 0; } int main() { int i,max=0,left,right; int ans; //读取数据 cin>>n>>f; f++; for (i=1; i<=n; i++) { cin>>a[i]; tj[i] = a[i]*a[i]*10000*PI; if (tj[i]>max) max=tj[i]; } //二分遍历查找 left=0; right=max; while(left+10<right) { int mid = (left+right)/2; if (IsOK((mid/10)*10)) left=mid; else right=mid; } if (left%10>=5) left = left/10 +1; else left = left/10; cout<<double(left/1000.0); //cin>>i; }
后面无论如何改都不行了,后来经看别人代码发现都是用的printf("%.3f")输出,
测试如下代码:
printf("%.1f", 3.45);
printf("%.1f", 3.445);
printf("%.1f", 3.5);
printf("%.1f", 3.55);
printf("%.3f", 3.44445);
printf("%.3f", 3.44455);
printf("%.3f",
3.44545);
printf("%.3f",
3.4454);
printf("%.3f",
3.4455);
我对c的格式化输出,程序的四舍五入彻底没了一个正确的推论,最后抛弃*10000这种放大策略,对实数
老老实实按照实数计算
-----------------------------------
AC代码
/#include <StdAfx.h> #include <stdlib.h> #include <stdio.h> #include <iostream> using namespace std; #define PI 3.141592653589793 int n,f; unsigned int a[10001]; double tj[10001]; int IsOK(double res) { int i,sum=0; for (i=1;i<=n;i++) { sum += int(tj[i] / res); } if (sum>=f) return 1; else return 0; } int main() { int i; double max,left,right; double ans; //读取数据 cin>>n>>f; f++; max=0; for (i=1; i<=n; i++) { cin>>a[i]; tj[i] = a[i]*a[i]*PI; if (tj[i]>max) max=tj[i]; } //二分遍历查找 left=0; right=max; while(right-left>1e-5) { double mid = (left+right)/2; if (IsOK(mid) ) left=mid; else right=mid; } if (IsOK(left)) ans=left; printf("%.3f",ans); //cin>>i; }---------------------------------
总结:1、其实这也是在解空间里面深搜枚举,只不过恰好有个符合特征的二分答案策略
2、在求解方程式 1+x=2+5 我们可以从上往下:x=2+5-1=6
也可以知道x为整数[0,10000]里面遍历枚举看哪个满足 if (x+1==2+5) {cout<<x;return;}
这正是枚举搜索,计算机傻瓜式工作的强项
3、本题坑点:有人说关于pi=3.1415926 精度不够会错5个点 所以pi=3.141592653589793 或者写出pi=cos(-1){math.h好像}
格式化输出,四舍五入,对于这种实数题目就不要渴望 while(left<right)中间差值为1,实数的比较本来就是一个精度为题
计算机里面的 0.0000000000000009 其实也是0 ,
While(right-left>1e-5) 基本就对了 如果3.14153基本等于3.14152
有人说while(right-left>1e-5)写成while(right-left>1e-8) 会超时,没测试
无论怎么着,感觉,思路简单,细节不少
---------------------------------------------------------------
其中多有不当之处,请批评指正
相关文章推荐
- noi题库(noi.openjudge.cn) 1.7编程基础之字符串T31——T35
- noi题库(noi.openjudge.cn) 1.13编程基础之综合应用 T12 分数求和
- noi题库(noi.openjudge.cn) 1.8编程基础之多维数组T21——T25
- noi.openjudge.cn 题目用到的头文件
- noi.openjudge 1711 潜伏者答案及其分析
- noi题库(noi.openjudge.cn) 1.11编程基础之二分查找T01、02、04
- 【二分查找】[Openjudge noi 1.11 09]膨胀的木棍
- 【DP】 noi openjudge 2.6 山区建小学
- Openjudge-NOI题库-蛇形填充数组
- NOI openjudge 8436:Saving Tang Monk
- openjudge-noi-2.6-2718:移动路线
- 【搜索】—深搜遍历 noi openjudge 2.5 A Knight's Journey
- 【贪心】 noi openjudge 4.6 Ride to Office
- Openjudge-NOI题库-出书最多
- NOI.OPENJUDGE 1.13.26 感想
- NOI OpenJudge 7627:鸡蛋的硬度
- NOI openjudge 6043:哆啦A梦的时光机(双向宽搜)
- OPENJUDGE NOI 8471 切割回文
- openjudge-noi-4.6-1455:An Easy Problem
- 【DP背包问题1】 noi openjudge 2.6 采药