Skip to content

Commit d4a1390

Browse files
authored
Merge branch 'master' into add_1079
2 parents 6d9bf82 + b1ff7e3 commit d4a1390

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

md/1038.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
ข้อนี้มีภารกิจ $N \leq 20$ ภารกิจ โดยต้องทำทุกภารกิจเพียงแต่ต้องเลือกลำดับที่จะทำ
2+
3+
หากทำภารกิจที่ $j$ เป็นลำดับที่ $i$ จะมีโอกาสสำเร็จ $a_{(j,i)}$ โจทย์ถามว่าผลคูณความน่าจะเป็นเหล่านี้ที่เป็นไปได้มากสุดคือเท่าไหร่
4+
5+
### แนวคิด
6+
7+
ข้อนี้เป็นโจทย์ Bitmask Dynamic Programming นั่นคือเป็นโจทย์ Dynamic Programming ที่เก็บ State เป็น Bitmask
8+
9+
สังเกตว่าเราสามารถเก็บคำตอบของแต่ละ State เป็น $DP[S]$ ซึ่งแทนผลคูณความน่าจะเป็นที่จะสำเร็จโดยที่ภารกิจที่สำเร็จแล้วคือ $S$ เมื่อ State $S=(b_{N}b_{N-1}\dots b_0)_2$ เป็นเลขฐานสองโดยที่ $b_j=1$ ถ้าเราทำภารกิจที่ $j$ แล้ว คำตอบจะเป็น $DP[2^N -1]$ เพราะ $2^N-1 = (11\dots1)_2$ (มี 1 $N$ ตัว)
10+
11+
เช่นถ้า $S=1010_2$ แสดงว่าทำภารกิจที่ 2 กับ 4 แล้ว
12+
13+
สังเกตว่าสำหรับ State $S$ จำนวนภารกิจที่ทำไปแล้วจะเท่ากับจำนวน bit ที่เป็น $1$ ให้จำนวนนี้เป็น $i_{S}$
14+
15+
สำหรับ $DP[0]$ สามารถตั้งเป็น 100 แทนโอกาส 100%
16+
17+
ในการคำนวณ $DP[S]$ สังเกตว่าจะต้องมีงานอันภารกิจ $j$ ที่ $b_j=1$ ใน $S$ ดังนั้นสามารถพิจารณาทีละงาน $j$ ดังกล่าวว่าผลคูณที่ดีที่สุดที่เป็นไปได้คือเท่าไหร่ ซึ่งจะได้ว่าเป็น $a_{(j, i_{S})} DP[S - (1<<j)]$ นั่นคือผลคูณของความน่าจะเป็นเมื่อทำงาน $j$ เป็นลำดับที่ $i$ กับผลคูณความน่าจะเป็นที่มากสุดที่เป็นไปได้สำหรับ $S - (1<<j)$ (ซึ่งเป็น State $S$ ก่อนทำภารกิจที่ $j$)
18+
19+
ดังนั้นสำหรับแต่ละ $S$ หากมีค่า $DP[0], \dots, DP[S-1]$ แล้วจะใช้เวลาคำนวณเพียง $\mathcal{O}(N)$ เมื่อพิจารณาทีละภารกิจ
20+
21+
ดังนั้นเมื่อต้องพิจารณา $2^N$ State เวลาทั้งหมดที่ใช้คือ $\mathcal{O}(N2^N)$
22+
23+
#### ตัวอย่างโค้ด
24+
25+
```cpp
26+
dp[0] = 100.0;
27+
for (int s = 1; s <= ((1 << n) - 1); s++) {
28+
int i = 0;
29+
for (int j = 0; j < n; j++)
30+
i += (((1 << j) & s) != 0);
31+
32+
dp[s] = 0;
33+
for (int j = 0; j < n; j++)
34+
if (((1 << j) & s) != 0)
35+
dp[s] = max(dp[s], dp[s ^ (1 << j)] * a[i - 1][j] / 100.0);
36+
}
37+
```
38+
39+
ตามคำอธิบายสำหรับแต่ละ $S$ จะนับจำนวนภารกิจที่สำเร็จแล้วใน State $S$ จากนั้นจะไล่ภารกิจที่สำเร็จใน $S$ ว่าควรทำอันไหนเป็นลำดับที่ $i$

md/2030.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
ข้อนี้ให้ Array $a_1, a_2, \dots, a_N$ $(N \leq 1000000)$ และให้หาจำนวน Subarray ที่มีพิสัย (ค่ามากสุด - ค่าต่ำสุด) อยู่ในช่วง $[p,q]$
2+
3+
### แนวคิด
4+
5+
ข้อนี้เป็นโจทย์ Sliding Window
6+
7+
อย่างแรกสังเกตว่าเราสามารถคำนวณจำนวนลำดับย่อยที่มีพิสัยในช่วง $[p,q]$ เป็น (จำนวนลำดับย่อยที่มีพิสัยไม่เกิน $q$) - (จำนวนลำดับย่อยที่มีพิสัยไม่เกิน $p-1$) ดังนั้นสำหรับข้อนี้เราจะพิจารณาการหาจำนวนลำดับย่อยที่มีพิสัยไม่เกิน $q$
8+
9+
ในจำนวนลำดับย่อยที่มีพิสัยไม่เกิน $q$ เราสามารถพิจารณาจำนวนลำดับย่อยที่เข้าข่ายที่จบที่แต่ละ $a_i$ หากนำจำนวนนี้มาบวกกันสำหรับทุก $a_i$ จะได้คำตอบที่ต้องการ
10+
11+
สังเกตว่าสำหรับ $m <n$ ถ้า Subarray $a_m, a_{m+1}, \dots, a_i$ มีพิสัยไม่เกิน $q$ จะได้ว่า $a_n, a_{n+1}, \dots, a_i$ มีพิสัยไม่เกิน $q$ เช่นกัน (เพราะค่ามากสุดจะไม่เกินและค่าต่ำสุดจะไม่น้อยกว่าของ Subarray ที่เริ่มที่ $m$) ดังนั้นสำหรับแต่ละ $i$ เราเพียวต้องหาค่า $m_i$ ที่เป็นค่าที่ต่ำสุดที่ทำให้ $a_{m_i}, \dots, a_i$ มีพิสัยไม่เกิน $q$ กล่าวอีกแบบคือ $\max(a_{m_i}, \dots, a_i) - \min(a_{m_i}, \dots, a_i) \leq q$
12+
13+
สังเกตได้อีกว่าสำหรับ $i<j$ จะได้ว่า $m_i \leq m_j$ ทั้งนี้เพราะถ้า $a_m, a_{m+1}, \dots, a_j$ มีพิสัยไม่เกิน $q$ $a_m, a_{m+1}, \dots, a_i$ ซึ่งเป็น Subarray จะต้องมีพิสัยในช่วงนั้นเช่นกัน
14+
15+
แนวคิดหลักของข้อนี้คือเราสามารถใช้วิธี Sliding Window ในการหาแต่ละ $m_i$ โดยจะใช้ Sliding Window สำหรับค่า max หนึ่งอันและ Sliding Window สำหรับ min อีกอัน (ผู้ที่ไม่คุ้นกับ Sliding Window สามารถอ่านได้จาก https://programming.in.th/tasks/1157/solution) โดย Sliding Window จะสามารถช่วยเราหาค่า max และ min ใน Window ที่เลื่อนไปทางขวาเรื่อยๆ ได้ในเวลา Amortized $\mathcal{O}(N)$ สำหรับทั้ง $N$ ช่องใน Array
16+
17+
เราจะ Initialize $l$ ซึ่งแทนจะ $m_i$ ในแต่ละขั้นเป็น 0 จากนั้นสำหรับแต่ละ $i=1$ ถึง $i=N$ ในแต่ละขั้นจะเริ่มจากการ push ดัชนี $i$ เข้าไปใน Window ทั้งสอง เพิ่ม $l$ และ pop ดัชนีใน Sliding Window ที่อยู่ก่อน $l$ จน $\max(a_{l}, \dots, a_i) - \min(a_{l}, \dots, a_i) \leq q$ เมื่อได้ $m_i=l$ ที่ถูกต้องแล้วจะได้ว่าจำนวน Subarray ที่มีพิสัยไม่เกิน $q$ ที่จบที่ $a_i$ คือ $i-l+1$
18+
19+
เนื่องจากการ Iterate ทุกค่าและใช้ Sliding Window ทั้งสองจะใช้เวลา $\mathcal{O}(N)$ ข้อนี้จึงใช้เวลาทั้งหมด $\mathcal{O}(N)$
20+
21+
#### ตัวอย่างโค้ด
22+
23+
```cpp
24+
long long range_leq_q(int q, const vector<int> &a) {
25+
deque<int> d_max;
26+
deque<int> d_min;
27+
28+
long long res = 0;
29+
long long l = 0;
30+
for (int i = 0; i < a.size(); i++) {
31+
while (d_max.size() > 0 && a[d_max.back()] <= a[i])
32+
d_max.pop_back();
33+
d_max.push_back(i);
34+
35+
while (d_min.size() > 0 && a[d_min.back()] >= a[i])
36+
d_min.pop_back();
37+
d_min.push_back(i);
38+
39+
while (l <= i && a[d_max[0]] - a[d_min[0]] > q) {
40+
l++;
41+
42+
while (d_max.size() > 0 && d_max[0] < l)
43+
d_max.pop_front();
44+
45+
while (d_min.size() > 0 && d_min[0] < l)
46+
d_min.pop_front();
47+
}
48+
res += (i - l + 1);
49+
}
50+
return res;
51+
}
52+
```
53+
54+
นี่คือโค้ดตัวอย่างสำหรับการหาจำนวน Subarray ที่มีพิสัยไม่เกิน $q$
55+
56+
เราจะเก็บ Sliding Window สองอันคือ `d_min` กับ `d_max` และเก็บค่า $l$ ที่อธิบายไดว้
57+
58+
ในแต่ละขั้นจะ push $i$ เข้า Sliding Window ทั้งสองโดยเอาข้อมูลที่สำคัญแล้วออก (ต่ำกว่าค่าปัจจุบันสำหรับ `d_max` หรือ สูงกว่าค่าปัจจุบันสำหรับ `d_min`)
59+
60+
จากนั้นจะเพิ่มค่า $l$ และ pop ค่าที่อยู่ก่อน $l$ ในแต่ละ Window จนพิสัยไม่เกิน $q$

0 commit comments

Comments
 (0)