2012-11-29 21:04:56Morris

[UVA][烏龜塔][Greedy解] 10154 - Weights and Measures

Sample Input:
101 101
100 201
99 300
98 301
5 302

10 50
100 120

10 1000
20 1000
30 40

3 5
2 4
1 2
1 1

10 40
20 100
30 40
Sample Output:
4
2
3
3
3

請容許上面抽像輸入格式,以上幾筆會比較有問題,在此先提出來。

為什麼再次討論這個題目,因為我的演算法特別期中考,考了這一道題目,先說說這兩個問題是等價的。
以下是我初略地想要證明。

給定多個工作的 (deadline, time),求能安排的最多個數。

(1)   max(deadline-time) Greedy是錯誤的?

(2)   min(time) Greedy 是錯誤的?

 

解決:

(1)   由定理t1+t2+t3 … +tk <= dk ,猜想由於 t1+t2+t3 … +tk-1 <= dk - tk,因此可以揣測 max(d*-t*),每次挑最大的出來,放入最佳解的解集合S中,為什麼這想法會錯,因為沒有考慮到 t 的效應,是最大的可能會導致個數下降。

I.          只能放入集合首的反例 (4,9) (6,10)
初始集合 S = { },第一步放入 (4,9),得 S = {(4,9)},第二步無法放入 (6,10) 因為 6+4 > 9
最佳解 S = {(4,9),(6,10)}

II.         只能放入集合尾的反例 (4,10) (6,9)
初始集合 S = { },第一步放入 (4,10),得 S = {(4,10)},第二步無法放入 (6,9) 因為 4+6 > 9
最佳解 S = {(4,10),(6,9)}

III.       可以插入隨機位子的反例 (3,15) (2,12) (6,15) (3,10) (1,8) (1,7) (1,6) (1,5) (1,4) (1,3) (1,2)
初始集合 S = { },第一步放入(3,15),得S = {(3,15)},第二步放入(2,12),得S = {(2,12)(3,15)},第三步放入(6,15),得S = {(2,12)(6,15)(3,15)},第四步放入(3,10),得{(3,10)(2,12)(6,15)(3,15)},第五步放入(1,8),得S ={(1,8)(3,10)(2,12)(6,15)(3,15)},行程滿 1+3+2+6+3 = 15 <= 15
最佳解 S = {(1,2)(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(3,10),(2,12),(3,15)}

(2)   策略 min(t*) 逐漸放入最佳解之中。

I.          只能放入集合首的反例 (10,40) (20,100) (30,40)
初始集合 S = { },第一步放入 (10,40),得 S = {(10,40)},第二步放入(20,100),得S = {(20,100),(10,40)},第三步無法放入(30,40),因為30+20+10 > 40
最佳解 S = {(30,40),(10,40),(20,100)}

II.         只能放入集合尾的反例 (10,100) (20,100) (30,40)
初始集合 S = { },第一步放入 (10,100),得 S = {(10,100)},第二步放入(20,100),得S = {(10,100),(20,100)},第三步無法放入(30,40),因為10+20+30 > 40
最佳解 S = {(30,40),(10,100),(20,100)}

III.       可以插入隨機位子的反例
目前找不到


後來策略 min(t*) 發現是正確的做法,為什麼正確?因為如果不把最小值放入其中,可以得到一個最優解,那麼必然存在同樣個數的最優解可以把最小值放入,那麼可以斷定最小值一定存在最優集中。

然後 UVa 的測資不夠強,也只不過一測測資,因此我很不信邪地繼續拿我的代碼進行數五萬筆的嘗試,試圖從比較 greedy 和 dp 的作法中,找到一個是錯的,但後來發現找不到。

請容許我不會證明。



#include <stdio.h>
#include <algorithm>
using namespace std;
struct TURTLE {
    int w, s; //<weight, strength>
};
int cmp(TURTLE a, TURTLE b) {
    if(a.w != b.w)
        return a.w < b.w; // weight small->large
    return a.s < b.s;
}
int main() {
    int n = 0, i, j;
    int Ssum, Sn = 0; //total weight
    TURTLE S[5700]; //<optimal solution>
    TURTLE T[5700];
    while(scanf("%d %d", &T[n].w, &T[n].s) == 2)
        n++;
    sort(T, T+n, cmp);
    for(i = 0; i < n; i++) {
        if(Sn == 0) {
            S[Sn++] = T[i];
            continue;
        }
        for(j = 0, Ssum = 0; j < Sn; j++)
            Ssum += S[j].w;
        int ins_pos = -1;
        for(j = Sn-1; j >= -1; j--) {
            if(j+1 == Sn || Ssum + T[i].w <= S[j+1].s) {
                if(j+1 != Sn)
                    Ssum -= S[j+1].w;
                if(Ssum + T[i].w <= T[i].s) {
                    if(j+1 == Sn || S[j+1].s >= T[i].s) {
                        ins_pos = j+1;
                        //printf("%d %d\n", i, ins_pos);
                    }
                }
            } else
                break;
        }
        if(ins_pos != -1) {
            for(j = Sn-1; j >= ins_pos; j--)
                S[j+1] = S[j];
            S[ins_pos] = T[i];
            Sn++;
            /*for(j = 0; j < Sn; j++)
                printf("(%d, %d)", S[j].w, S[j].s);
            puts("");*/
        }
    }
    printf("%d\n", Sn);
    return 0;
}