1. 程式人生 > >POJ-1505&&UVA-714 抄書(貪心+二分)

POJ-1505&&UVA-714 抄書(貪心+二分)

題目傳送門http://poj.org/problem?id=1505
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=655
題目大意:
有k個本書,m個人,第i本書有ai頁,m個人想把書抄完,但是誰都不想讓其中一個人偷懶,讓你在不改變書的順序的情況下把書分成m份,使得所有人中分到的書的頁數的最小值儘量的大,測試資料有多組。
輸入
第一行輸入一個整數n,表示有n組測試資料;每組測試資料包含行,第一行有兩個整數k,m(1 <= k <= m <= 500).第二行有k個整數,第i個表是第i本書的頁數。
輸出


輸出k個整數,並用 ‘/’ 把分書的方式表示出來,如果有多組,則讓第一人的書儘量少,如果還有多組,讓第二人的書儘量少,以此類推
Sample Input
2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100
Sample Output

100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100

困擾我6個小時的小bug,第一次發,紀念一下。
解題思路: 套路1:求解最小值的最大值,一般二分答案來進行。
二分模板

int check(int mid)
{
    if(***) return 1;
    return 0;
}
int
solve(int l,int r) { while(l<=r) { int mid=l+(r-l)>>1; if(check(mid)) r=mid-1; else l=mid+1; } return l-1; }

貪心原則:儘量滿組最右邊的人,讓他拿到的頁數儘量的多。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace
std; int a[550]; int vis[550]; int k,m; int check(int x) { int sum=0; int c=1; for(int i=k-1;i>=0;i--) { if(a[i]+sum > x) { c++; sum=a[i]; if(c > m) return 0; } else sum += a[i]; } return 1; } void print(int x) { int sum=0; int co=1; for(int i=k-1;i>=0;i--) { if(sum+a[i] > x) { sum=a[i]; co++; vis[i]=1; } else sum += a[i]; if(m-co == i+1) { for(int j=0 ; j <= i ;j++) vis[j] = 1; break; } } for(int i=0;i<k-1;i++) { printf("%d ",a[i]); if(vis[i]) printf("/ "); } printf("%d\n",a[k-1]); } int main() { int n; scanf("%d",&n); while(n--) { memset(vis,0,sizeof(vis)); scanf("%d%d",&k,&m); long long sum=0; long long l=-1,r=0; for(int i=0;i<k;i++) { scanf("%d",&a[i]); if(l < a[i]) l = a[i]; r += a[i]; } while(l<=r) { long long mid=(l+r)/2; if(check(mid)) r=mid-1; else l=mid+1; } print(l); } return 0; }

題目不是很複雜,但是我提交了自我以為正確了,提交了20+次,全部WA。最後看了一整個下午才看出來。這裡是貪心法的判斷與二分之間的一個小小的衝突,就是對於二分的下界要求一定要是所有資料中的最大值,因為如果比其小的話,有可能在某個二分值判斷的時候剛剛好加上某個本身就大於mid的值,這樣在劃分的時候就會有這個大比mid大的值獨自分在一組,但事實上這種情況是不合法的,當有這樣的數出現時,就行該判斷出而分出的答案小了。