bzoj1233 单调队列优化dp
https://www.lydsy.com/JudgeOnline/problem.php?id=1233
數據結構優化dp的代碼總是那么抽象
題意:奶牛們討厭黑暗。 為了調整牛棚頂的電燈的亮度,Bessie必須建一座干草堆使得她能夠爬上去夠到燈泡 。一共有N大包的干草(1<=N<=100000)(從1到N編號)依靠傳送帶連續的傳輸進牛棚來。第i包干草有一個 寬度W_i(1<=w_i<=10000)。所有的干草包的厚度和高度都為1. Bessie必須利用所有N包干草來建立起干草堆,并且按照他們進牛棚的順序擺放。她可以相放多少包就放 多少包來建立起tower的地基(當然是緊緊的放在一行中)。接下來他可以放置下一個草包放在之前一級 的上方來建立新的一級。注意:每一級不能比下面的一級寬。她持續的這么放置,直到所有的草包都被安 置完成。她必須按順序堆放,按照草包進入牛棚的順序。說得更清楚一些:一旦她將一個草包放在第二級 ,她不能將接下來的草包放在地基上。 Bessie的目標是建立起最高的草包堆。
Input
第1行:一個單一的整數N。 第2~N+1行:一個單一的整數:W_i。
?
奧妙重重dp題。
第一眼覺得是個反向貪心,從上往下取,每次取最小的可以形成下一層的干草集合,后來發現是個假算法。
反例?
6?
6 2 3 7 10 11
既然貪心不行,就自然而然想到dp,考慮dp[i]表示到這一層的最大層數,發現不可行,因為這樣的dp方法不能固定一個狀態。
但是這一題有一個很強的證明:對于一座干草堆而言,基底最小的狀態一定為最優狀態。
也就是說,每一個狀態中,地基最小的狀態就是高度最高的狀態,我們可以簡單的推斷一下,每一個高度最高的狀態必然可以從下往上貪心的堆干草最終達到基底最小的狀態。
這樣我們原來的dp就變成了dp[i]表示i -- n種所有干草組成的集合形成的最小的干草堆的基底。
我們反向從N - > 1遍歷,對于i,需要找到一個最小的大于i的j滿足 sum[j - 1] - sum[i - 1] >= dp[j], 這樣就變成了一個O(N * N)的dp,但是這還不夠,到這一步需要考慮優化。
我們將轉移方程變形 變成sum[i - 1] <= sum[j - 1] - dp[j],注意到我們每次需要的考慮的集合在每一次詢問i的時候都多加入1個,以及當存在k < j && sum[k - 1] - dp[k]? >= sum[j - 1] - dp[j]的時候,j就是一個多余的狀態,sum[i - 1]由于其前綴和的性質,事實上是遞減的,對于每一次集合內的查找,我們在查找到最小的j的時候,可以將其余的比sum[i - 1]大的狀態全部舍棄。
以上三個性質中,顯然可以發現用來維護這個集合的數據結構就是單調隊列,最終時間復雜度O(n)
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,tmp,K; int a[maxn]; int sum[maxn]; int dp[maxn]; int f[maxn]; int Queue[maxn]; int main() {int N; Sca(N); //sum[i - 1] <= sum[j - 1] - dp[j]For(i,1,N){Sca(a[i]);sum[i] = sum[i - 1] + a[i];} int head = 1,tail = 1; Queue[1] = N + 1;_For(i,N,1){while(head < tail && sum[i - 1] <= sum[Queue[head + 1] - 1] - dp[Queue[head + 1]]) head++;dp[i] = sum[Queue[head] - 1] - sum[i - 1];f[i] = f[Queue[head]] + 1;while(head <= tail && sum[i - 1] - dp[i] >= sum[Queue[tail] - 1] - dp[Queue[tail]]) tail--;Queue[++tail] = i;}printf("%d",f[1]);#ifdef VSCodesystem("pause");#endifreturn 0; }?
轉載于:https://www.cnblogs.com/Hugh-Locke/p/9671948.html
總結
以上是生活随笔為你收集整理的bzoj1233 单调队列优化dp的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习深度学习视频资料大汇总
- 下一篇: Axure8.0 装汉化包