1. 程式人生 > >[POJ 3122] Pie 二分答案+貪心

[POJ 3122] Pie 二分答案+貪心

題目大意:我和朋友們在生日宴會上分享蛋黃派,我們每個人都需要得到相同大小的派,並且每個人只能從一個完整的派上面切下一定大小的派(可以是完整的一塊,也可以是切下來的一小塊,不過不能由數個切下來的派拼湊而成)。求符合條件時,我們能得到派的面積的最大值。

輸入一共有 T 組資料,每組資料的第一行為一個 N 和 F,代表 N 份派和 F 個朋友 (即:一共有 F+1 個人,包含自己);接下來的一行有 N 個數,代表每個派的半徑。
輸出則為每組資料對應的最大面積,答案的誤差最多在 10^-3 之內。

題目分析:
一道比較入門的二分答案題。
由題,每個人最多能得到一個完整派,並且每個派都是等大的;最少則什麼也得不到。因此,我們可以對此進行二分。
取 mid 為每個人能獲得的最大面積與最小面積的中間值,如果當前的 mid 能使多於 F 個人得到這麼大的派,說明每個人可能還會得到更大面積的派,那麼往上查詢;否則,當 mid 不能的時候,每個人得不到這麼大面積的派,就往下查詢。最終,我們就可以確定每個人得到的派面積的大小。可以證明這樣做是正確的。

下面附上程式碼:

/* Author:ArcCCcp MEM:284KB TIME:63MS 未加優化*/

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MX=10005;
const double PI=3.1415926535897932;
const double ME=1e-5;        //二分查詢的最小範圍,小於這個範圍的查詢就顯得沒有必要

int n,f;
double size[MX];             //每個派的大小
int main(){ int T; cin>>T; while (T--){ memset(size,0,sizeof(size)); cin>>n>>f; f++; //包括自己 double low=0.0,high=0.0; for (int i=1;i<=n;i++){ cin>>size[i]; size[i]*=size[i]; //這裡我們不選擇乘上π的值,而是先儲存半徑的平方,避免浮點運算精度誤差過大
if (high < size[i]) high=size[i]; } double mid=(high+low)/2.0; while (high-low > ESP){ //二分答案 int tot=0; mid=(high+low)/2.0; for (int i=1;i<=n;i++){ tot+=(int)(size[i]/mid); } if (tot < f) high=mid; else low=mid; //如果分到當前大小的派的人達到了f,則說明可能還有分的更大的方法,往上查詢; //如果沒有達到f,那麼當前面積太大,往下查詢; } printf("%0.4lf\n",mid*PI); } return 0; }