1. 程式人生 > >[tsinsen1315]積木(沈添笑)——動態規劃+單調棧

[tsinsen1315]積木(沈添笑)——動態規劃+單調棧

題目大意:

搭積木是xx最喜歡的遊戲之一。xx有n塊高低不同的積木,她將它們排成一列。xx希望積木看起來儘可能的整齊,她將相鄰兩塊積木高度之差的絕對值之和乘上係數c定義為積木序列的混亂值,顯然,混亂值越小越好。xx可以通過調整積木的高度使其混亂值變小,她可以花費t^2的代價,往某塊積木上再搭一塊高為t(t為任意自然數)的積木,在同一塊積木上只能搭一次。 xx想考考你,混亂值與花費之和的最小值是多少呢?

思路:

考慮高度的dp顯然很不優秀,所有我們要想辦法去除高度的影響。 假設最終序列中[l,r]中,只有l,r的高度沒有發生變化,那麼一個結論是[l+1,r-1]的高度必定都是相同的,簡單地證明: 如果最後地序列中高度不全部相同,那麼我們可以把[l+1,r-1]中最高的減少1單位,那麼答案肯定是會更優的。 於是我們可以考慮一個n

2n^2的dp: dp[i]=min(dp[j]+k=j+1i1(xh[k])2+(h[i]+h[j]x×2)×c)dp[i]=\min(dp[j]+\sum_{k=j+1}^{i-1}(x-h[k])^2+(h[i]+h[j]-x\times 2)\times c) 後面的式子可以二次函式來O(1)O(1)求解最小值。 (ij1)×x22×(k=j+1i1h[k]+c)×x+(k=j+1i1h2[k]+(h[i]+h[j])×c)(i-j-1)\times x^2-2\times(\sum_{k=j+1}^{i-1}h[k]+c)\times x+(\sum_{k=j+1}^{i-1}h^2[k]+(h[i]+h[j])\times c)
於是考慮怎麼優化列舉,不難發現只有當maxk=j+1i1h[k]min(h[i],h[j])\max_{k=j+1}^{i-1}h[k]\leq \min(h[i],h[j])時,轉移才有可能更優。比每一個點高的點只會轉移一次,於是我們可以維護一個單調遞減的棧,每一次在棧中轉移比它矮的點,同時將元素一個一個彈出,直到轉移到比它高的為止。

#include<bits/stdc++.h>

#define
REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll; using namespace std; void File(){ freopen("tsinsen1315.in","r",stdin); freopen("tsinsen1315.out","w",stdout); } template<typename T>void read(T &_){ T __=0,mul=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')mul=-1; ch=getchar(); } while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar(); _=__*mul; } const int maxn=1e6+10; const ll inf=LLONG_MAX>>1; int n; ll c,h[maxn],sum[maxn],sum2[maxn],dp[maxn],ans=inf; stack<int>stk; void init(){ read(n); read(c); REP(i,1,n)read(h[i]); REP(i,1,n){ sum[i]=sum[i-1]+h[i]; sum2[i]=sum2[i-1]+h[i]*h[i]; } } void transfer(int j,int i,ll Min){ if(i==j+1)return; ll a=(i-j-1),Max=min(h[j],h[i]); ll b=-2*(sum[i-1]-sum[j])-((j!=0)+(i!=n+1))*c; ll d=sum2[i-1]-sum2[j]+(h[j]*(j!=0)+h[i]*(i!=n+1))*c; ll x=round(-1.0*b/2/a); x=min(max(x,Min),Max); dp[i]=min(dp[i],dp[j]+a*x*x+b*x+d); } void work(){ memset(dp,63,sizeof(dp)); h[0]=h[n+1]=inf; dp[0]=0; stk.push(0); REP(i,1,n+1){ dp[i]=dp[i-1]+(i!=1 && i!=n+1)*abs(h[i]-h[i-1])*c; ll Max=0; while(stk.size()!=1 && h[stk.top()]<=h[i]){ transfer(stk.top(),i,Max); Max=h[stk.top()]; stk.pop(); } transfer(stk.top(),i,Max); stk.push(i); } printf("%lld\n",dp[n+1]); } int main(){ File(); init(); work(); return 0; }