[tsinsen1315]積木(沈添笑)——動態規劃+單調棧
阿新 • • 發佈:2018-12-14
題目大意:
搭積木是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單位,那麼答案肯定是會更優的。 於是我們可以考慮一個的dp: 後面的式子可以二次函式來求解最小值。 於是考慮怎麼優化列舉,不難發現只有當時,轉移才有可能更優。比每一個點高的點只會轉移一次,於是我們可以維護一個單調遞減的棧,每一次在棧中轉移比它矮的點,同時將元素一個一個彈出,直到轉移到比它高的為止。
#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;
}