# Uva 10026: Shoemaker's problem ###### tags: `貪心法` > 鞋匠有 $N$ 項工作要完成,他知道自己每項工作需要多少工作天能夠完成\ > 以及每個工作延誤一天需要繳多少延遲的費用\ > 此外,延遲天數計算是從當天算到該工作開始的那天(意味著第一件工作沒罰金)\ > 對了! 他每天只能做一件工作,不是多工的哦!<br><br> > 題目有給一項測試資料 > 以這個測資來說\ > 如果工作順序為 $1, 2, 3, 4$ > 則罰金: $$0 \times 4 + 1000 \times 3 + 4 \times 2 + 6 \times 5 = 3038$$ > 反之,如果工作順序為 $2, 1, 3, 4$ > 則罰金: $$0 \times 1000 + 1 \times 4 + 4 \times 2 + 6 \times 5 = 42 $$ > 這個問題的目標是幫助鞋匠找出能使罰金最少的工作順序 **Source:** [題目傳送門](https://onlinejudge.org/index.php?option=onlinejudge&Itemid=8&page=show_problem&problem=967) {%pdf https://vj.z180.cn/67c23d3e823e0df266b139a6166f9322?v=1595137800%} --- ## *Original* ### 題目敘述 > Shoemaker has $N$ jobs (orders from customers) which he must make. > Shoemaker can work on only one job in each day. <br><br> > For each $i-th$ job, it is known the integer $T_i (1 \leq T_i \leq 1000)$, the time in days it takes the shoemaker to finish the job. <br><br> > For each day of delay before starting to work for the $i-th$ job, shoemaker must pay a fine of $S_i (1 \leq S_i \leq 10000)$ cents. ### Question > *Your task is to help the shoemaker, writing a program to find the **sequence of jobs** with **minimal** total fine.* ### Input format > The input begins with a single **positive integer** on a line by itself indicating the number of the **cases** following, each of thme as described below.<br><br> > This line is followed by a blank line, and there is also a **blank line between two consecutive inputs**.<br><br> > First line of input contains as integer $N (1 \leq N \leq 1000)$.\ > The next $N$ lines each contain two numbers: > * time (needs) > * fine (fine / day) ### Output format > For each test case, the output must follow the description below.<br><br> > The outputs of two consecutive cases will be separated by a blank line.<br><br> > Your program should print the sequence of jobs with **minimal** fine. Each job should be represented by its number in input. \ > All integers should be placed on only one output line and separated by one space.<br><br> > If multiple solutions are possible, print the first **lexicographically**. ### Sample Input 1 4 3 4 1 1000 2 2 5 5 ### Sample Output 2 1 3 4 --- ## 概念 * 貪心 * 思考一個排序方法 * 要怎麼找基準值 * 穩定的排序法可以確保按照字典序排序 --- ## 主要思路 * 像是機會成本的概念,我選擇先做哪一個工作,此時其它工作就會累計罰金,所以要找一個方式衡量目前選擇的重要性 * 對於適當排序的狀況,兩個相鄰的工作 $a, b$,若順序為 $ab$ 或 $ba$ ,會注意到除了 $ab$ 以外,其餘工作的罰金並不會改變,也就是說關鍵在於選 $ab$ , 選 $ba$ 哪個損失比較小 * 假設如下: |Time|Fine| |:--:|:--:| |$a_d$ |$a_f$| |$b_d$ |$b_f$| * $\fbox{case 1: a->b} \; a_d \times b_f$ * $\fbox{case 2: b->a} \; b_d \times a_f$ In the first case, the inequality should be correct: $$a_d \times b_f \leq b_d \times a_f$$ 同除以 $b_f \times a_f$: $$\frac{a_d}{a_f} \leq \frac{b_d}{b_f}$$ * 同上述結果,會發現兩兩比較時,只需要將 $\frac{a_d}{a_f}$ 數值由小到大排序即可 * 字典序的問題則只需要選擇穩定的排序法,確保相同值時不會更動順序即可 --- ## 程式碼 ```cpp= #include <iostream> #include <cstring> using namespace std; typedef struct S_job{ double important; int32_t order; }s_job; //按照重要程度去排序 int32_t compare(const void *a, const void *b){ if ((((s_job *)a)->important) > (((s_job *)b)->important)) return 1; return 0; } int main(){ int32_t t = 0; cin >> t; //拿來吃blank line的 string temp; for (int32_t i = 0 ; i < t ; i ++){ if (i != 0) cout << endl; getline(cin, temp); int32_t num = 0; cin >> num; s_job arr[num]; for (int32_t j = 0; j < num ; j++){ int32_t day = 0 , pen = 0; cin >> day >> pen; arr[j].order = j+1; arr[j].important = (double)day / pen; } qsort(arr, num, sizeof(s_job), compare); //blank要處理一下,其餘輸出即可 int32_t blank = 0 ; for (int32_t j = 0 ; j < num ; j++){ if (blank) cout << " "; cout << arr[j].order; blank = 1; } cout << endl; } return 0; }