林尚廷
No Code No Life :: A competitive programmer
理由 1. 我好爛QQ
理由 2. 見下一頁
蕭梓宏 <3
希望能以經典題演示與實際解題為主
若備課不充分敬請多多包涵 ><
可是... 抄自己的不算抄吧QQ
而且我有偷改一下啦
分治法 - 將問題拆分成小問題,再一一解決
Divide:拆成相同結構的小問題
Conquer:遞迴解決小問題
Combine:利用小問題的答案解決更大的問題
求 \(a^b \bmod m\)
\(a, b\leq10^{18}, m\leq10^9\)
Divide:
先計算 \(a^{\lfloor\frac{b}{2}\rfloor}\bmod m\) 的值
Conquer:
遞迴解決小問題
Combine:
\(a^b \equiv (a^{\lfloor\frac{b}{2}\rfloor})^2 \pmod m\)
如果 \(b\) 是奇數則答案要再乘上 \(a\)
Combine:
\(a^b \equiv (a^{\lfloor\frac{b}{2}\rfloor})^2 \pmod m\),如果 \(b\) 是奇數則答案要再乘上 \(a\)
\(T(n) = T(\lfloor\frac{n}{2}\rfloor) + O(1)\)
\(T(n) = O(\log n)\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
int power(int a, int b, int m){
if(!b) return 1;
int x = power(a, b>>1, m);
if(b&1) return ((x*x)%m * a)%m;
else return (x*x)%m;
}
int main(){
int a, b, m;
cin >> a >> b >> m;
cout << power(a%m, b, m) << endl;
}
#include <bits/stdc++.h>
#define int long long
using namespace std;
int power(int a, int b, int m, int ans = 1) {
while(b) {
if(b & 1) ans *= a, ans %= m;
a *= a, a %= m, p >>= 1;
}
return ans;
}
int main() {
int a, b, m;
cin >> a >> b >> m;
cout << power(a%m, b, m) << endl;
}
你現在有三根柱子跟\(R\)個大小不一的圓環,一開始所有圓環都套在第一根柱子上,且較小的圓環在上,疊成一個塔。現在你要將所有圓環都移動到第三根柱子,每次你可以從任意柱子的最上方拿取一個圓環放到任意的另一根柱子上,但是在過程中大圓環不能疊在小圓環上,你有辦法將這個過程輸出嗎?
Divide:
拆解 - solve(x, from, to)印出將x個圓盤的塔從柱子from移到柱子to的過程
Conquer:
遞迴解決小問題
Combine:
將除了最下面的圓環塔統統移開,再將最下面的圓環移至目的地,最後再把上面的圓環塔移回去
Combine:
將除了最下面的圓環塔統統移開,再將最下面的圓環移至目的地,最後再把上面的圓環塔移回去
\(T(n) = 2T(n-1) + O(1)\)
\(T(n) = O(2^n)\)
#include <bits/stdc++.h>
using namespace std;
void move(int r, int from, int to){
if(r == 1){
cout << from << ' ' << to << endl;
return;
}
int relay = 6 - from - to;
move(r - 1, from, relay);
move(1, from, to);
move(r - 1, relay, to);
}
int main(){
int n;
cin >> n;
move(n, 1, 3);
}
題敘略
邊界條件:
如果原序列長度是1,則不用排已有序
Divide:
將序列拆成左右兩邊
Conquer:
遞迴解決小問題 - 將左右兩邊排序
Combine:
將左右兩個排好的序列合成一個排好的序列
Combine:將左右兩個排好的序列合成一個排好的序列
5 3 6 8 7 0 9 1 2 4
3 5 6 8 7 0 1 2 4 9
0 1 2 3 4 5 6 7 8 9
Conquer:
遞迴解決小問題 - 將左右兩邊排序
Combine:
將左右兩個排好的序列合成一個排好的序列
\(T(n) = 2T(n) + O(n)\)
\(T(n) = O(n\log n)\)
#include <bits/stdc++.h>
using namespace std;
int a[500010], temp[500010];
void merge_sort(int, int);
int main(){
int n;
cin >> n;
for(int i=0; i<n; i++) cin >> a[i];
merge_sort(0, n);
for(int i=0; i<n; i++) cout << a[i] << ' ';
cout << endl;
}
void merge_sort(int l, int r){
if(l+1 == r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid, r);
int lptr = l, rptr = mid, ptr = l;
while(lptr < mid || rptr < r){
if(lptr != mid && (rptr == r || a[lptr] < a[rptr]))
temp[ptr++] = a[lptr++];
else temp[ptr++] = a[rptr++];
}
for(int i=l; i<r; i++) a[i] = temp[i];
}
// sorting an array with ascending order
sort(arr, arr+n);
// sorting an array with custom comparison
sort(arr, arr+n, cmp);
// sorting a vector with ascending order
sort(vec.begin(), vec.end());
// sorting a vector with custom comparison
sort(vec.begin(), vec.end());
給你一個數列\(a_n\),問有幾組數對\((i, j)\)滿足\(i<j, a_i>a_j\)
也就是問逆序數對的個數啦
邊界條件:
如果原序列長度是1,則沒有任何逆序數對
Divide:
將序列拆成左右兩邊
Conquer:
遞迴解決小問題 - 算出左右兩個區間的逆序數對數
Combine:
將左右兩邊的答案以及跨過兩邊的答案相加
Combine:將左右兩邊的答案以及跨過兩邊的答案相加
5 3 6 8 7 0 9 1 2 4
3 5 6 8 7 0 1 2 4 9
0 1 2 3 4 5 6 7 8 9
對一個數列\(S\)來說,若\(S\)的第\(i\)項\(s_i\)與第\(j\)項\(s_j\)符合\(s_i>s_j\),並且\(i<j\)的話,那麼我們說\((i,j)\)是一個逆序數對。請問給定\(S\),總共有多少個逆序數對呢?
給一個長度\(2^N\)的數列,現在你可以對這個序列使用兩種操作:(1)將左邊\(2^{N-1}\)個數字拿掉;(2)將右邊\(2^{N-1}\)個數字拿掉,每次操作結束之後數列長度都會變成一半。你可以使用這兩種操作任意多次,而使得序列變成一個遞增序列。請問這個遞增序列的長度最長是多少? \((N \leq 16)\)
一個有偶數層的河內塔,有\(a, b, c\)三根柱子,假設所有的環原本在\(a\)柱上,請將奇數號的環移到\(b\)柱上,偶數號的環移到\(c\)柱上,大的環不能疊在小的環上,請輸出移動過程和最少步數。
有人請你構造一個鬼腳圖出來,而他會給你一些要求,例如從左數來的第一個點要走到從左數來的第三個點、左數來的第四個點要走到從左數來的第二個點...
你的任務是輸出他給定的限制最少要幾個橫槓才能完成,讓他可以挑一個最客家的方式拿到他想要的鬼腳圖。
最多有\(5\times 10^5\)個點
給平面上\(N\)個點的座標,求距離最近的兩個點的距離到小數點第六位
有一個數列\(<a_i>\),總共有\(N (\leq10^6)\)個整數,求數字總和最大的一段區間的總和。(區間可以為空,空區間的總和為0)
給你一個數列\(S\),一個該數列的連續和(Continuous Sum,以下簡稱CS)是指\(S\)當中的某些連續項之總和。
很容易算得出來,一個總長度為項的數列\(S\),其連續和(CS)共有 \(\frac{n(n+1)}{2}\) 個。 注意,問題來囉!
請問,這 \(\frac{n(n+1)}{2}\) 個連續和(CS)之中,第\(k\)大的是多少?
提示:做好前綴和之後對答案二分搜
則題目就轉化為:
對於所有 \(j > i\),滿足 \(S[j] - S[i] > ans\) 的 \(i, j\) 有幾個?
是否小於\(k\)個?
display port
拆成相同結構的小問題
解決小問題
紀錄小問題的最優解
利用小問題的答案解決更大的問題
-- DP = DQ + Memoization --
動態規劃是1950年代,Richard Bellman 等人在研究多階段決策過程(Multistage Decision Process)時發明的優化問題方法。但他的上司不喜歡理論的研究,所以當他發明這方法時就取了一個跟數學無關的名字,也就是現在常聽到的 Dynamic Programming。
\(a_1 = a_2 = 1\)
\(a_n = a_{n-1} + a_{n-2}\) \((n \leq 3)\)
#include <bits/stdc++.h>
using namespace std;
int fib(int n){
if(n <= 1) return n;
return fib(n-1) + fib(n-2);
}
int main(){
int n;
cin >> n;
cout << fib(n) << endl;
}
#include <bits/stdc++.h>
using namespace std;
int FIB[51];
int fib(int n){
if(n <= 1) return n;
if(FIB[n]) return FIB[n];
return FIB[n] = fib(n-1) + fib(n-2);
}
int main(){
int n;
cin >> n;
cout << fib(n) << endl;
}
#include <bits/stdc++.h>
using namespace std;
int FIB[51];
int main(){
int n;
cin >> n;
FIB[1] = FIB[2] = 1;
for(int i=3;i<n;i++){
FIB[i] = FIB[i-1] + FIB[i-2];
}
cout << FIB[n] << endl;
}
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, a, b, c;
cin >> n;
a = b = 1;
for(int i=3;i<n;i++){
c = a + b, a = b, b = c;
}
cout << c << endl;
}
滾動、矩陣快速冪...
決定狀態
列出轉移式
打好基底 (邊界)
決定狀態:dp[i] - 費氏數列第 i 項
列出轉移式:dp[i] ← dp[i-1] + dp[i-2]
打好基底 (邊界):dp[1] = dp[2] = 1
最優子結構:這個狀態的最優解仰賴子問題的最優解,也就是說狀態的定義時常是某狀態的最大/最小值或直接是某問題的答案。
無後效性:若求出dp[3]=2,那dp[3]永遠是2,不會因為要求不同的dp[i]而改變dp[3]的值。
重複子問題:計算dp[4]可能要用到dp[3],問題重複了,不需要重算一次dp[3]。
有一個數列\(<a_i>\),總共有\(N (\leq10^7)\)個整數,求數字總和最大的一段區間的總和。(區間可以為空,空區間的總和為0)
1. 枚舉所有區間,逐一加起來,\(O(n^3)\)
2. 前綴求區間和,\(O(n^2)\)
3. 分治,\(O(n\log n)\)
感覺都會TLE,試試看\(O(n)\)DP吧!
決定狀態:
dp[i] - 以第 i 個數字結尾(或空區間)的最大區間和
列出轉移式:
dp[i] ← max(0, dp[i-1] + a[i])
打好基底 (邊界):
dp陣列初始化成0就好了
這樣一來我們最後將所有dp值取max就是答案了
#include <bits/stdc++.h>
using namespace std;
int a[10000010], dp[10000010];
int main(){
int n;
cin >> n;
for(int i=0; i<n; i++) cin >> a[i];
for(int i=0; i<n; i++)
dp[i] = max(0, dp[i-1] + a[i]);
cout << *max_element(dp, dp+n) << endl;
}
有一個數列\(<a_i>\),總共有\(N (\leq10^7)\)個整數,求數字總和最大的一段區間的總和。(區間不可以為空)
蕭電有\(N\)個硬幣,但是他的硬幣來自特殊國度,所以會有奇怪的面額,算起錢來也特別麻煩。假設第\(i\)個硬幣的面額是\(a_i\)元,而商品的價格是\(p\)元,請問蕭電能夠在不需找零的情況下購買此商品並付清嗎?\((N\leq10^4, a_i\leq10^4)\)
1. 決定狀態:
dp[i][j] - 只利用前 i 個硬幣能否湊出 j 元
2. 列出轉移式:
dp[i][j] ← dp[i-1][j-a[i]] OR dp[i-1][j]
3. 打好基底 (邊界):dp[0][0] ← true
根據狀態定義,dp[N][p] 就是答案!
1. 決定狀態:
dp[i][j] - 只利用前 i 個硬幣能否湊出 j 元
重要觀念 - 空間複雜度:
\(i\leq10^4, j\leq10^4\)
空間複雜度約 \(4\times10^8\) bytes
狀態佔太多空間!
2. 列出轉移式:
dp[i][j] ← dp[i-1][j-a[i]] OR dp[i-1][j]
可以發現取得dp[i]的任何一個值只要用到dp[i-1]這條陣列。我們最後只要dp[N][p],所以在計算dp[i]時,dp[i-2]以前的數值都可以忽略!
所以我們可以重複使用陣列,這就是滾動dp。
2. 列出轉移式:
dp[i][j] ← dp[i-1][j-a[i]] OR dp[i-1][j]
我們可以重複使用陣列,這就是滾動dp,
滾動dp很簡單,直接把維度拔掉就行了。
dp[j] ← dp[j-a[i]] OR dp[j]
#include <bits/stdc++.h>
using namespace std;
int a[10010], dp[10010];
int main(){
int n, p;
cin >> n >> p;
dp[0] = 1;
for(int i=1; i<=n; i++) cin >> a[i];
for(int i=1; i<=n; i++)
for(int j=p; j>=0; j--)
if(j-a[i] >= 0)
dp[j] = max(dp[j], dp[j-a[i]]);
cout << dp[p] << endl;
}
蕭電有\(N\)個硬幣,假設第\(i\)個硬幣的面額是\(a_i\)元、重量是\(w_i\)克,他想要將總重量不超過\(W\)克的硬幣送給女朋友致瑄當生日禮物,請問蕭電最多能送給女朋友多少錢呢?\((N\leq10^4, a_i\leq10^4)\)
蕭電有\(N\)個硬幣,假設第\(i\)個硬幣的面額是\(a_i\)元、重量是\(w_i\)克,他想要將總重量不超過\(W\)克的硬幣送給女朋友致瑄當生日禮物,請問蕭電要拿哪幾個硬幣才能送給女朋友最多錢呢?\((N\leq10^3, a_i\leq10^3)\)
這裡有\(N\)個人排成一列,每個人的身高都不一樣,我們假設第\(i\)個人的身高是\(h_i\)。現在你要在這列人中挑選一些人出來,而且你想要這些人之中越右邊的人身高要越高才行。在這樣的條件下,你最多可以挑選出多少人呢? \((N\leq2000, h_i\leq10^9)\)
決定狀態:
dp[i] - 最右邊為左邊第 i 個人的LIS長度
列出轉移式:
dp[i] ← \(\max\limits_{j<i, h_j<h_i}\){dp[j]} + 1
打好基底 (邊界):
dp陣列初始化成0就好了嘍
#include <bits/stdc++.h>
using namespace std;
int dp[2010], h[2010];
int main(){
int n;
cin >> n;
for(int i=0; i<n; i++) cin >> h[i];
for(int i=0; i<n; i++){
for(int j=0; j<i; j++)
if(h[j] < h[i]) dp[i] = max(dp[i], dp[j]);
dp[i]++;
}
cout << *max_element(dp, dp+n) << endl;
}
改變限制:\(N\leq10^5\)
這題其實有\(O(n\log n)\)的解法!
讓我們改變一下DP方式吧!
換一種狀態:
dp[i][j] - 前\(i\)個數字中所有長度為 \(j+1\) 的遞增子序列最右邊數字的最小值
列出轉移式:
dp[i][j] ← \(\min\limits_{j = 0 或 dp[i-1][j-1]<a[i]}\){a[i]}
打好基底 (邊界):
dp陣列初始化成INF (很大的數字)
換一種狀態:
dp[i][j] - 前\(i\)個數字中所有長度為 \(j+1\) 的遞增子序列最右邊數字的最小值
列出轉移式:
dp[i][j] ← \(\min\limits_{j = 0 或 dp[i-1][j-1]<a[i]}\){dp[i-1][j], a[i]}
明顯看到 i 這個維度可以重複利用,把它拔掉
dp[j] ← \(\min\limits_{j = 0 或 dp[j-1]<a[i]}\){dp[j], a[i]}
換一種狀態:
dp[j] - 前\(i\)個數字中所有長度為 \(j+1\) 的遞增子序列最右邊數字的最小值
列出轉移式:
dp[j] ← \(\min\limits_{j = 0 或 dp[j-1]<a[i]}\){dp[j], a[i]}
1. 不管 i 是多少,dp[j] 隨著 j 增加而增加
2. 對於每個 i,轉移時用到 a[i] 的 j 只有一個,其餘都是從 dp[j] 轉移來
二分搜從 a[i] 轉移的 j!
#include <bits/stdc++.h>
using namespce std;
int a[100010], dp[100010];
int main(){
int n;
cin >> n;
for(int i=0; i<n; i++) cin >> a[i];
fill(dp, dp+n, 1e9);
for(int i=0; i<n; i++)
*lower_bound(dp, dp+n, a[i]) = a[i];
cout << lower_bound(dp, dp+n, 1e9) - dp << endl;
}
這裡有\(N\)個人排成一列,每個人的身高與能力都不一樣,我們假設第\(i\)個人的身高是\(h_i\)。現在你要在這列人中挑選一些人出來,而且你想要這些人之中越右邊的人身高要越高才行。在這樣的條件下,你需要挑選出哪些人呢? \((N\leq2000, h_i\leq10^9, a_i\leq10^9)\)
這裡有\(N\)個人排成一列,每個人的身高與能力都不一樣,我們假設第\(i\)個人的身高是\(h_i\)、能力是\(a_i\)。現在你要在這列人中挑選一些人出來,而且你想要這些人之中越右邊的人身高要越高才行。在這樣的條件下,你挑選出的這些人能力總和最大是多少呢? \((N\leq2000, h_i\leq10^9, a_i\leq10^9)\)
給一個小於13的整數\(n\),輸出\(n!\)的值。
給一個有數字構成的三角形,現在請問從最頂端走到最底端最大的和是多少。
* 每個點只能往左下或右下走,底層的點不能再往下走。
* 三角形的高度介於1到100之間。
* 三角形上的數字都介於0到99之間。
你要用許多\(1\times 2\)的磚頭蓋一個\(2\times n\)的磚牆,總共可以蓋出幾種花樣呢?(磚塊可以直放、橫放,磚塊不可切割且一定要鋪滿磚牆) \((n\leq 50)\)
In how many ways can you tile a \(3\times n\) rectangle with \(2\times1\) dominoes? \((n\leq 30)\)
請問要將字串A改成字串B最少需要多少次操作
(|A|, |B| \(\leq 1000\))
Tips: getline(cin, s);
給你長度為N的一個正整數序列,請你求出最長嚴格遞增子序列的長度。
所謂嚴格遞增子序列,是指去掉序列中的某些數字之後,剩下的子序列是嚴格遞增的。
\(n\)個相同的箱子要放入\(m\)個不同的球,總共有幾種放法?
\(5 \leq n, m \leq 200\)
有\(N\)個俄羅斯娃娃,第\(i\)個娃娃的高度是\(h_i\)、寬度是\(w_i\)。如果一個娃娃的寬度與高度都小於另一個,那麼就可以將兩個娃娃嵌套。你的任務是根據這些寬度以及高度算出最多可以嵌套多少層的娃娃。\((N\leq 20000)\)
教練想要挑幾個人組隊打籃球,他希望挑到的人身高總和越大越好。現在所有選手兩個兩個並排成兩排,每一排都是\(N\)個人。教練不喜歡選到連續的人,所以只要有一個人被選到,他的前後或旁邊的人都不能再選。現在輸入每個人的身高,求教練能選到的最大身高總和。\((N \leq 10^5)\)
給一個\(N\)個數字的序列,你每次可以選擇一個不是最左邊或最右邊的數字\(a_i\)把它消除,不過這樣需要花費 \(a_{i-1}\times a_i \times a_{i+1}\) 的代價,直到序列只剩下左右兩個數字為止。求操作完成後的最小花費。\((N \leq 50)\)
有\(n\)段路,每段路有一個分數\(a_i\),你每段路可以用其中一種速度
1.用走的:你不會得到任何分數
2.用跑的:你會得到\(a_i\)的分數
3.用衝的:你會得到\(2a_i\)的分數,但你下一段路得用走的
請問你最多能得到多少分? \((n \leq 50)\)
火場裡有\(N\)個人\((N \leq 100)\),每個人因為被困在不同地方所以有不同救援難度,假設拯救第\(i\)個人所需的時間是\(t_i\)。因為火勢的蔓延,第\(i\)個人要趕在時間點\(d_i\)以前被救出來。由於救援隊私心的緣故,他們偷偷將每個人賦予權重\(p_i\),並且希望被拯救的那些人權重和越大越好。你能知道哪些人終會得救嗎?\((t_i \leq 20, d_i \leq 2000, p_i\leq 20)\)
有一排\(n\)個方塊,每個方塊都有顏色,每次可以把連續顏色的一段消掉,若消去的方塊有\(L\)個,則可以得到\(L^2\)的分數,請問全部消完最多可以得到幾分?\((n \leq 200)\)
有天爸爸交代小明幫忙把寫好的信裝進信封裡,信與信封上的名字要配對。例如:給王大毛的信要裝到寫有王大毛的信封。這時頑皮的小明想到一個惡作劇,就是把所有人的信與信封都裝錯;也就是說沒有一個人會收到正確寄給自己的信。例如:A收到B的信,B收到C的信,C收到A的信。
請幫小明算算,到底有多少種裝法可以不讓任何人收到應該寄給自己的信。(最多20人)
有兩個人在\(N\times M\)的格子裡找寶藏,每個格子可能是寶藏、障礙物或空地。兩個人都由左上角進,右下角出,且每次只能往右或往下。若兩人可以分開行動,且一個寶藏只能拿一次,問兩人總共最多可以拿到多少寶藏。\((N, M \leq 100)\)
給你一個氣泡排序的code以及一個\(N\)個數的序列,你現在有\(N\)個點但沒有邊的圖,當氣泡排序執行中兩數每進行一次交換,就將編號是這兩數的頂點連一條邊。求排序完成後此圖的最大點獨立集大小。\(N\leq 10^5\)
有三個人(P, E, C)走在一條筆直的路徑上,路徑上依序有\(N\)顆石塊排成一排\((N \leq 2\times 10^6)\),且每一顆都只能被特定其中一個人撿起。之後P、E、C會各選一個區間將區間內所有可以被他撿起的石塊都拿起來。他們所選定的區間兩兩交集必須要是空的。
說石持那十塊,P、E、C三個人已經走完這條路並且撿好石塊了。請計算他們三人所持有的石塊個數總和最大是多少。
給一個字串\(S\),求其中一個最長的回文子序列,如果長度超過1000就只要輸出長度為1000的回文子序列即可\((|S| \leq 200000)\)
這題時限卡很緊,空間也卡很緊
給兩個字串A, B,請輸出這兩個字串的其中一個最長公共子序列 (|A|, |B| \(\leq 10000\))
這題時限跟空間又卡更緊了
給定一張有向帶權圖,求權重和最小的漢米爾頓迴圈(Hamiltonian Cycle)
不妨設 \(dp[S][v]\) 為從
"已經訪問過\(S\)裡面所有點,且現在位於\(v\)點上"
這個狀態經過剩下所有頂點回到頂點\(0\)的最小權重和。
那麼答案就是 \(dp[空集合][0]\)
決定狀態:
對於所有不在\(S\)裡面的點\(u\),都可以走過去
然後看看走到哪裡會比較划算
\(dp[S][v] = \min\{dp[S \cup \{u\}][u] + cost(v, u)\}\)
列出轉移式:
若經過所有點之後又回到\(0\),則權重和是\(0\):
\(dp[全][0] = 0\)
剩下的狀態就用很小的數字(e.g. -1e18)等待更新
打好基底:
二進制表示法:用整數表達集合的方法
例如有
{1, 2, 5, 6} = \(2^1 + 2^2 + 2^5 + 2^6\)
{0, 1, 4, 7, 6} = \(2^0 + 2^1 + 2^4 + 2^7 + 2^6\)
如此一來每一個整數都對應到一個集合
二進制表示法的性質:
好用的暴力枚舉技巧 有點離題了QQ
for(int i=0;i<(1<<n);i++) {
// 枚舉所有集合
}
int sub = sup;
do {
// 枚舉sup的所有子集
sub = (sub - 1) & sup;
} while(sub != sup);
int tmp = (1 << k) - 1;
while(tmp < (1 << n)) {
// 枚舉大小為k的集合
int x = tmp & -tmp, y = tmp + x;
tmp = ((tmp & ~y) / x >> 1) | y;
}
#include<bits/stdc++.h>
using namespace std;
int dp[1 << 16][16];
signed main() {
for(int S=0;S<(1<<n);S++) fill(dp[S], dp[S] + n, -1e18);
dp[(1 << n) - 1][0] = 0; // 目標
for(int S=(1<<n)-1-1;S>=0;S--) {
for(int v=0;v<n;v++) for(int u=0;u<n;u++) {
if(!(S >> u & 1)) {
dp[S][v] = min(dp[S][v], dp[S | 1 << u][u] + d[v][u]);
}
}
}
cout << dp[0][0] << endl;
}
大家應該都看過題目了吧
如果大家都AC了就跳過吧XD
第 \(i\) 列
01001 = 9
枚舉一整列的所有開關情形相當於枚舉 \(0\) 到 \(2^N-1\) 的所有整數
11010 = 26
第 \(i+1\) 列
#include<bits/stdc++.h>
using namespace std;
int dp[16][1 << 16], ans;
signed main() {
for(int msk=0;msk<(1 << n);msk++) dp[0][msk] = 1;
for(int i=1;i<n;i++) {
for(int msk=0;msk<(1 << n);msk++) {
for(int pre=0;pre<(1 << n);pre++) {
if(valid(msk, pre)) dp[i][msk] += dp[i-1][pre];
}
}
}
for(int msk=0;msk<(1 << n);msk++) ans += dp[n-1][msk];
cout << ans << endl;
}
\(dp[i][msk]\) = 只考慮第\(i\)列以上的窗戶,且第\(i\)列的開關狀態剛好是\(msk\)的方法數
如果改成一格一格做
\(dp[i][j][msk]\) = ?
沒錯就是這樣 可以發現利用計算好的所有\(dp[i][j][pre]\)很好計算\(dp[i][j+1][msk]\)
會MLE,所以至少得滾動一個維度
塞到矩陣裡
地鼠基地是一個長型的基座,基座上每隔一公尺就會有一個地鼠洞。玩家站在這個基地的最左邊,與第一個地鼠洞相距一公尺。
已知由左而右編號為\(i\)的地鼠洞每\(T_i\)秒地鼠會出現一次,且玩家移動一公尺需要一秒鐘。若被打中的地鼠便不再出現,求將所有地鼠打完所需的最少秒數。
提示:洞不多,最多16個
某校有\(M\)種不同的課程,其中有些課程的時間會有衝堂。如果給定每組有衝堂的課程,且知道學校中總共有\(N\)間不同的教室,請問共有多少種安排各課程上課教室的方式?最少要用到幾間教室?\((N, M \leq 10)\)
提示:我看不懂題目
如果說一個數列s符合
\(s_0=a\)
\(s_1=b\)
\(s_n=xa_{n-2}+ya_{n-1} (2\leq n)\)
那我們就稱這個數列為索拉數列(sola sequence)
求索拉數列第\(n\)項
其中\(n\)不會大於\(1,000,000,000\)
而\(a, b, x, y\)則會小於\(2^{32}\)
螞蟻在一張由鍋子跟筷子組成的無向圖上走,且走每條邊的機率都一樣。已知螞蟻從每個鍋子當作起點的機率都一樣,求螞蟻走了\(T\)步後停在第\(X\)個鍋子上的機率是多少。
鍋子數\(N\leq100\),\(T\leq 10^9\)。
一個正方形的鎮區分為 \(N\times N\) 個小方塊。農場位於方格的左上角,集市位於左下角。小優穿過小鎮,從左上角走到左下角,剛好經過每個方格一次。他想要知道有多少走法。\((N \leq 10)\)
有 \(N\times N\) 的農田,每塊農田種有不同價值的蘿蔔。但是相鄰八格的蘿蔔無法一起被拔起,求能夠拔起的最大價值總和。\((N \leq 22)\)
By 林尚廷
動態規劃入門、分而治之初探