0w1

UVA 1422 Processor ( 二分搜 + 置點問題 )

UVa Online Judge
馬上會想到二分搜最大速度,而判斷的部分就會變成經典的置點問題。每個點 ( 作業 )都有左界和右界,只能將它置於其中。一個經典的算法是先將每個點以其左界限制做排序,然後由左到右依次考慮每個位置該放置哪個點,這部分可以利用優先佇列,每次將佇列裡右界最小的取出來做掉。如果把該位置的資源用盡後作業仍然有剩,則更新剩餘的工作量後再丟進去一次。如果在過程中優先佇列取出來的點的右界已過期,或者迴圈結束後有任何點還沒考慮過,或者還在優先佇列裡沒有出來,則判斷無解。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4 + 4;
const int MAXRD = 2e4 + 4;
const int MAXW = 1e3 + 3;

struct Work{
    int r, d, w;
    Work(){}
    void read(){
        scanf("%d%d%d", &r, &d, &w);
    }
    bool operator < (const Work &oth) const{
        return d > oth.d;
    } // pq get least d
} work[MAXN];

bool cmp_l(const Work &a, const Work &b){
    return a.r < b.r;
}

int n;

bool ok(int x){
    priority_queue<Work> pq;
    int k = 0;
    for(int i = 1; i <= MAXRD; ++i){
        while( work[ k ].r < i && k < n ) pq.push( work[ k++ ] );
        int space = x;
        while( space > 0 && !pq.empty() ){
            Work c = pq.top(); pq.pop();
            if( c.d < i ) return false;
            if( c.w <= space ) space -= c.w;
            else{
                c.w -= space;
                space = 0;
                pq.push( c );
            }
        }
    }
    if( k == n && pq.empty() ) return true;
    return false;
}

void solve(){
    sort( work, work + n, cmp_l );    
    int lb = 1, rb = MAXN * MAXW;
    int ans = -1;
    while( lb <= rb ){
        int mid = ( lb + rb ) / 2;
        if( ok( mid ) ) ans = mid, rb = mid - 1;
        else lb = mid + 1;
    }
    assert( ans >= 0 );
    printf("%d\n", ans);
}

int main(){
    int T; scanf("%d", &T);
    while( T-- ){
        scanf("%d", &n);
        for(int i = 0; i < n; ++i)
            work[ i ].read();
        solve();
    }
    return 0;
}