貪心
貪心演算法的核心為,採取在目前狀態下最好或最佳(即最有利)的選擇。
貪心演算法雖然能獲得當前最佳解,但不保證能獲得最後(全域)最佳解,提出想法後可以先試圖尋找有沒有能推翻原本的想法的反例,確認無誤再實作。
Scarecrow
UVa 12405 - Scarecrow
有一個 的稻田,有些稻田現在有種植作物,為了避免被動物破壞,需要放置稻草人,稻草人可以保護該塊稻田和左右兩塊稻田,請問最少需要多少稻草人才能保護所有稻田?
從左到右掃描稻田,如果第 塊稻田有作物,就把稻草人放到第 塊稻田,這樣能保護第 塊稻田,接著從第 塊稻田繼續掃描。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12 | int main(){
string s;
int i, n, t, tc = 1;
cin >> t;
while (t--){
cin >> n >> s;
int nc = 0;
for (i = 0; i < n; i++)
if (s[i] == '.')i += 2, nc++;
cout << "Case " << tc++ << ": " << nc << endl;
}
}
|
霍夫曼樹
霍夫曼樹
給定 個有權重 的葉節點,請構出一顆有 個葉節點的二元樹 , 的 要最小, 代表葉節點的深度。
讓權重比較大的葉節點越上層越好。將每個點視為一棵樹,每次將最小權重的兩棵樹合併起來,直到最後合併成一棵樹。
UVa 10954 Add All
給定 個數,每次將兩個數 合併成 ,只到最後只剩一個數,合併成本為兩數和,問最小合併成本為多少。
建構霍夫曼樹的變形題。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | int main()
{
int n, x;
while (cin >> n, n)
{
priority_queue<int, vector<int>, greater<int>> q;
while (n--)
{
cin >> x;
q.push(x);
}
long long ans = 0;
while (q.size() > 1)
{
x = q.top();
q.pop();
x += q.top();
q.pop();
q.push(x);
ans += x;
}
cout << ans << endl;
}
}
|
Commando War
UVa 11729 - Commando War
有 個部下,每個部下要花 分鐘交待任務,再花 分鐘執行任務,一次只能對一位部下交代任務,但可以多人同時執行任務,問最少要花多少時間完成任務。
執行時間長的人先交代任務。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | struct Data
{
int b, j;
bool operator<(const Data &rhs) const { return j > rhs.j; }
};
int main()
{
int n, ti = 0;
Data a[1005];
while (cin >> n, n)
{
for (int i = 0; i < n; ++i)
cin >> a[i].b >> a[i].j;
sort(a, a + n);
int ans = 0, sum = 0;
for (int i = 0; i < n; ++i)
{
sum += a[i].b;
ans = max(ans, sum + a[i].j);
}
cout << "Case " << ++ti << ": " << ans << '\n';
}
}
|
刪數字問題
刪數字問題
給定一個數字 ,需要刪除 個數字,請問刪除 個數字後最小的數字為何?
第一種想法是將最大的數字移除,但這種想法存在反例()。
第二種想法是刪除滿足第 位數大於第 位數的最左邊第 位數,扣除高位數的影響較扣除低位數的大。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | int main()
{
string s;
int k;
cin >> s >> k;
for (int i = 0; i < k; ++i)
{
if ((int)s.size() == 0)
break;
int pos = (int)s.size() - 1;
for (int j = 0; j < (int)s.size() - 1; ++j)
{
if (s[j] > s[j + 1])
{
pos = j;
break;
}
}
s.erase(pos, 1);
}
while ((int)s.size() > 0 && s[0] == '0')
s.erase(0, 1);
if ((int)s.size())
cout << s << '\n';
else
cout << 0 << '\n';
}
|
換硬幣問題
換硬幣問題
給定硬幣幣值 and ,請問最少需要幾個硬幣才能湊出 元?
從大幣值的硬幣開始使用,如果用剩餘的再用更小的餘額,以此類推。
如果幣值之間沒有整除,要使用到動態規劃。
區間覆蓋相關問題
這類題目會需要一個紀錄起點、終點和支援排序的資料結構。
參考程式碼
| struct Line
{
int L, R;
bool operator<(const Line &rhs) const
{
...
}
};
|
區間覆蓋長度
zerojudge b966 - 區間覆蓋長度
給定 條線段區間為 ,請問這些線段的覆蓋所覆蓋的長度?
先將所有區間依照左界由小到大排序,左界相同依照右界由小到大排序,用一個變數 紀錄目前最大可以覆蓋到的右界。如果目前區間左界 ,代表該區間可以和前面的線段合併。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | struct Line
{
int L, R;
bool operator<(const Line &rhs) const
{
if (L != rhs.L)
return L < rhs.L;
return R < rhs.R;
}
};
int main()
{
int n;
Line a[10005];
while (cin >> n)
{
for (int i = 0; i < n; i++)
cin >> a[i].L >> a[i].R;
sort(a, a + n);
int ans = 0, L = a[0].L, R = a[0].R;
for (int i = 1; i < n; i++)
{
if (a[i].L < R)
R = max(R, a[i].R);
else
{
ans += R - L;
L = a[i].L;
R = a[i].R;
}
}
cout << ans + (R - L) << '\n';
}
}
|
最小區間覆蓋問題
最小區間覆蓋問題
給定 條線段區間為 ,請問最少要選幾個區間才能完全覆蓋 ?
先將所有區間依照左界由小到大排序,對於當前區間 ,要從左界 的所有區間中,找到有著最大的右界的區間,連接當前區間。
Codeforces 1066B - Heaters
長度 的直線中有數個加熱器,在 的加熱器可以讓 內的物品加熱,問最少要幾個加熱器可以把 的範圍加熱。
對於最左邊沒加熱的點 ,選擇最遠可以加熱 的加熱器,更新已加熱範圍,重複上述動作繼續尋找加熱器。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | int main()
{
int n, r;
int a[1005];
cin >> n >> r;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int i = 1, ans = 0;
while (i <= n)
{
int R = min(i + r - 1, n), L = max(i - r + 1, 0), nextR = -1;
for (int j = R; j >= L; --j)
{
if (a[j])
{
nextR = j;
break;
}
}
if (nextR == -1)
{
ans = -1;
break;
}
++ans;
i = nextR + r;
}
cout << ans << '\n';
}
|
最多不重疊區間
最多不重疊區間
給你 條線段區間為 ,請問最多可以選擇幾條不重疊的線段(頭尾可相連)?
依照右界由小到大排序,每次取到一個不重疊的線段,答案 。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | struct Line
{
int L, R;
bool operator<(const Line &rhs) const { return R < rhs.R; }
};
int main()
{
int t;
cin >> t;
Line a[30];
while (t--)
{
int n = 0;
while (cin >> a[n].L >> a[n].R, a[n].L || a[n].R)
++n;
sort(a, a + n);
int ans = 1, R = a[0].R;
for (int i = 1; i < n; i++)
{
if (a[i].L >= R)
{
++ans;
R = a[i].R;
}
}
cout << ans << '\n';
}
}
|
區間選點問題
區間選點問題
給你 條線段區間為 ,請問至少要取幾個點才能讓每個區間至少包含一個點?
將區間依照右界由小到大排序, 第一個區間的右界,遍歷所有區段,如果當前區間左界 代表必須多選一個點 (),並將 當前區間右界。
UVa 1615 - Highway
給定 個座標,要在 軸找到最小的點,讓每個座標至少和一個點距離 。
以每個點 為圓心半徑為 的圓 ,求出 和 軸的交點 ,題目轉變成區間選點問題。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | struct Line
{
double L, R;
bool operator<(const Line &rhs) const { return R < rhs.R; }
};
int main()
{
double L, d;
int n;
Line a[100005];
while (cin >> L)
{
cin >> d >> n;
for (int i = 0; i < n; ++i)
{
double x, y;
cin >> x >> y;
double r = sqrt(d * d - y * y);
a[i].L = max(0.0, x - r);
a[i].R = min(L, x + r);
}
sort(a, a + n);
int ans = 1, R = a[0].R;
for (int i = 1; i < n; ++i)
{
if(a[i].L > R)
{
R = a[i].R;
++ans;
}
}
cout << ans << '\n';
}
}
|
工作排程相關問題
最小化最大延遲問題
最小化最大延遲問題
給定 項工作,每項工作的需要處理時長為 ,期限是 ,第 項工作延遲的時間為 ,原本 為第 項工作的完成時間,求一種工作排序使 最小。
按照到期時間從早到晚處理。
證明
假設第 項工作和第 項工作相鄰 (),且 ,我們稱 是相鄰逆序對。
由上面三點可以推得交換是相鄰逆序對並不會增加 ,透過不斷交換相鄰逆序對,最後證明提出的想法是對的。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | struct Work
{
int t, d;
bool operator<(const Work &rhs) const { return d < rhs.d; }
};
int main()
{
int n;
Work a[10000];
cin >> n;
for (int i = 0; i < n; ++i)
cin >> a[i].t >> a[i].d;
sort(a, a + n);
int maxL = 0, sumT = 0;
for (int i = 0; i < n; ++i)
{
sumT += a[i].t;
maxL = max(maxL, sumT - a[i].d);
}
cout << maxL << '\n';
}
|
最少延遲數量問題
最少延遲數量問題/烏龜塔問題
給定 個工作,每個工作的需要處理時長為 ,期限是 ,求一種工作排序使得逾期工作數量最小。
期限越早到期的工作越先做。將工作依照到期時間從早到晚排序,依序放入工作列表中,如果發現有工作預期,就從目前選擇的工作中,移除耗時最長的工作。
上述方法為 Moore-Hodgson's Algorithm。
UVa 10154 - Weights and Measures
給定烏龜的重量和可承受重量,問最多可以疊幾隻烏龜?
和最少延遲數量問題是相同的問題,只要將題敘做轉換。
- 工作處裡時長 烏龜重量
- 工作期限 烏龜可承受重量
- 多少工作不延期 可以疊幾隻烏龜
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | struct Work{
int t, d;
bool operator<(const Work &rhs) const { return d < rhs.d; }
};
int main()
{
int n = 0;
Work a[10000];
priority_queue<int> pq;
while(cin >> a[n].t >> a[n].d)
++n;
sort(a, a + n);
int sumT = 0, ans = n;
for (int i = 0; i < n; ++i)
{
pq.push(a[i].t);
sumT += a[i].t;
if(a[i].d<sumT)
{
int x = pq.top();
pq.pop();
sumT -= x;
--ans;
}
}
cout << ans << '\n';
}
|
任務調度問題
任務調度問題
給定 項工作,每項工作的需要處理時長為 ,期限是 ,如果第 項工作延遲需要受到 單位懲罰,請問最少會受到多少單位懲罰。
依照懲罰由大到小排序,每項工作依序嘗試可不可以放在 ,如果有空閒就放進去,否則延後執行。
Zerojudge a567 - 死線排程
給定 項工作,每項工作的需要處理時長為 ,期限是 ,如果第 項工作在期限內完成會獲得 單位獎勵,請問最多會獲得多少單位獎勵。
和上題相似,這題變成依照獎勵由大到小排序。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | struct Work
{
int d, p;
bool operator<(const Work &rhs) const { return p > rhs.p; }
};
int main()
{
int n;
Work a[100005];
bitset<100005> ok;
while (cin >> n)
{
ok.reset();
for (int i = 0; i < n; ++i)
cin >> a[i].d >> a[i].p;
sort(a, a + n);
int ans = 0;
for (int i = 0; i < n; ++i)
{
int j = a[i].d;
while (j--)
if (!ok[j])
{
ans += a[i].p;
ok[j] = true;
break;
}
}
cout << ans << '\n';
}
}
|
多機調度問題
多機調度問題
給定 項工作,每項工作的需要處理時長為 ,有 台機器可執行多項工作,但不能將工作拆分,最快可以在什麼時候完成所有工作?
將工作由大到小排序,每項工作交給最快空閒的機器。
參考程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | int main()
{
int n, m;
int a[10000];
cin >> n >> m;
for (int i = 0; i < n; ++i)
cin >> a[i];
sort(a, a + n,greater<int>());
int ans = 0;
priority_queue<int, vector<int>, greater<int>>pq;
for (int i = 0; i < m && i < n; ++i)
{
ans = max(ans, a[i]);
pq.push(a[i]);
}
for (int i = m; i < n; ++i)
{
int x = pq.top();
pq.pop();
x += a[i];
ans = max(ans, x);
pq.push(x);
}
cout << ans << '\n';
}
|
相關題目