poj1505Copying Books 二分+貪心詳細總結
前兩天花了時間理解了nyoj的586瘋牛和nyoj619青蛙過河,滿以為自己能重新寫出這道題。。。誰知道。。。。。
題意:有m本書,k個人來抄,每本書有一個書本頁數;求使得k個人抄完的最大頁數最小,並且每個人都至少要抄一本,然後輸出抄書的方案
分析:
這裡又涉及到前面nyoj的586瘋牛和nyoj619青蛙過河說過的最大值中的最小值, 用前面的例子去理解比較方便
1.我們應該先用二分+貪心算出一個最大頁數的最小值--這裡和前面的類似
在二分的過程中,我們對於當前考慮的值 x 劃分人數的貪心過程中,我們就有flag[i]去標記,這個位置應該劃分開,
同時要注意的是:根據題意Output 最後一句If there is more than one solution, print the one that minimizes the work assigned to the first scriber, then to the second scriber
etc. 我們可以考慮每一次劃分,我們從後面往前面劃分:
例如:
5 3
100 100 100 100 100
output
100 / 100 100 / 100 100
而不是
100 100 / 100 100 / 100
2.然後就是根據這個最大的最小值去劃分,並且,這裡要保證每個人都有書抄,那麼在二分+貪心過程中,可能出現得到的人數少於 k
例如:
5 4
100 100 100 100 100
二分的最大的最小值 為 200 那麼就會劃分成下面的樣子
100 / 100 / 100 /100 100 紅色加粗的那個劃分就不會出現,因為是按照200 來劃分的,那麼此時,我們就應該從左往右開始新增劃分,從左往右也是為了保證題意中要求前面的人抄的少的要求
上馬:碼上註釋:
個人愚昧觀點,歡迎討論與指正#include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define MAX 505 #define LL __int64 int M,k; int book[MAX]; bool flag[MAX];//用來標記 int cnt; int copy(LL x) { cnt = 1; LL sum = 0; memset(flag,false,sizeof(flag)); for(int i = M-1; i >= 0; i --) { sum += book[i]; if(sum > x) { cnt ++; sum = book[i]; flag[i] = true; } } return cnt; } void print() { cout<<book[0]; for(int i = 1; i < M; i ++) { if(flag[i-1]) cout<<" /"; cout<<' '<<book[i]; } cout<<endl; } int main() { int T; LL l,r; cin>>T; while(T--) { cin>>M>>k; l = r = 0; for(int i = 0; i < M; i ++) { cin>>book[i]; if(book[i] > l) l = book[i]; r += book[i]; } LL mid; while(l < r) { mid = (l+r)/2; if(copy(mid) <= k) r = mid;// - 1 else l = mid + 1; } //得再分一次,不然 5 4 // 100 100 100 100 100 //這個案例就會用上面的 l (不足200) 去切得到 100 / 100 / 100 / 100 / 100 int cnt = copy(r); //對應於下面的案例<2> 可能分得不足k次,那麼就把前面的每一本書分給一個人超(題目要求) for(int i = 0; i < M && cnt < k; i ++) { if(!flag[i]) flag[i] = true,cnt++; } print(); } return 0; } /* <1> 7 4 100 100 100 100 100 100 100 <2> 7 6 100 100 100 100 100 100 100 output 100 / 100 100 / 100 100 / 100 100 100 / 100 / 100 / 100 / 100 / 100 100 */