洛谷P1622 釋放囚犯(dp好題)
阿新 • • 發佈:2020-07-09
釋放囚犯
題目描述
\(Caima\) 王國中有一個奇怪的監獄,這個監獄一共有\(P\)個牢房,這些牢房一字排開,第\(i\)個緊挨著第\(i+1\)個(最後一個除外)。現在正好牢房是滿的。
上級下發了一個釋放名單,要求每天釋放名單上的一個人。這可把看守們嚇得不輕,因為看守們知道,現在牢房中的\(P\)個人,可以相互之間傳話。如果某個人離開了,那麼原來和這個人能說上話的人,都會很氣憤,導致他們那天會一直大吼大叫,搞得看守很頭疼。如果給這些要發火的人吃上肉,他們就會安靜點。
輸入格式和輸出格式
輸入格式
第一行兩個數\(P\)和\(Q\),\(Q\)表示釋放名單上的人數;
第二行\(Q\)個數,表示要釋放哪些人。
資料規模
對於\(100%\)的資料\(1≤P≤1000\); \(1≤Q≤100\);\(Q≤P\);且\(50%\)的資料 \(1≤P≤100\);\(1≤Q≤5\)
輸出格式
僅一行,表示最少要給多少人次送肉吃。
輸入和輸出樣例
輸入#1
20 3
3 6 14
輸出#1
35
本題思路
這道題看起來和一般的區間dp有很多出入,不是從小到大,而是從大到小,看起來要是分成不同的子問題,每個子問題還是會互相干擾的,不妨模擬一遍過程可以發現,好像和石子合併(不要問我什麼是石子合併)的過程剛好相反,說白了就是石子合併的逆過程,如果一個大區間中挑選一個切點,那麼就分成了兩個互不相干的子問題(因為只與切點有關,先選擇了切點之後分成的兩個區間就毫無關聯),再想,把分成的兩個小區間再次選切點,就又分成了兩個互不相干的兩個更小的子問題,依次類推,可以發現這和石子合併沒有任何區別,因此,這道題的大致思路沒問題了,我們可以推出轉移方程:\(f[i][j]=min{f[i][j],f[i][k-1]+f[k+1][j]+a[j+1]-a[i-1]-1-1}\)
最後一個\(-1\) 是什麼呢,就是第k個放出去的囚犯,不用給他吃肉了。
程式碼實現
#include<bits/stdc++.h> using namespace std; const int maxn = 1e3+50; int P, Q, a[maxn], dp[maxn][maxn]; inline int read(){ char ch = getchar(); int f = 1, x = 0; while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();} while(isdigit(ch)){x = x*10;x += ch - '0';ch=getchar();} return x*f; } int main(){ P = read(),Q = read(); for(int i = 1; i <= Q; i++){ a[i] = read(); } sort(a+1, a+Q+1); a[0] = 0; a[Q+1] = P+1; for(int len = 1; len <= Q; len++){ for(int l = 1; l + len - 1 <= Q; l++){ int r = l + len - 1; dp[l][r] = 0x3f3f3f3f; for(int j = l; j <= r; j++){ dp[l][r] = min(dp[l][r], dp[l][j-1] + dp[j+1][r] + a[r+1] - a[l-1] - 2); } } } printf("%d",dp[1][Q]); return 0; }