【學習筆記】初次學習斜率最佳化的程式碼及筆記

Ashkadira發表於2024-05-05

include<bits/stdc++.h>

using namespace std;
int n,m;
int dp[10000],s[6100],q[10000];
int slope(int j,int k)
{
int x=(dp[j]-dp[k]+s[j]s[j]-s[k]s[k])/(s[j]+s[k]);
return x;
}//求斜率
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]+=s[i-1];
}
int L=1,R=1;
dp[0]=0;
for(int i=1;i<=n;i++)
{
while(L<R && slope(q[L],q[L+1])<=s[i])L++;
dp[i]=dp[q[L]]+(s[i]-s[q[L]])(s[i]-s[q[L]])+m;
while(L<R && slope(q[R-1],q[R])>slope(q[R],i))R--;
q[++R]=i;
}
cout<<dp[n]<<endl;
}
return 0;
}
/
斜率最佳化:
1.將轉移方程抽象成一個不等式關係,
如本題中由於要求出最小价值,易知轉移方程為:dp[i]=min(dp[j-1]+(s[i]-s[j])^2+m) (1<=j<=i)
設此時有兩個點,j和k
則如果有dp[j]+(s[i]-s[j])2+m<dp[k]+(s[i]-s[k])2+m
即可知道j優於k
將上不等式展開可得:
dp[j]+s[i]2+s[j]2-2s[i]s[j]<dp[k]+s[i]2+s[k]2-2s[i]s[k];
移項:dp[j]-dp[k]+s[j]2-s[k]2<2s[i]s[j]-2s[i]s[k]
整理:(dp[j]-dp[k]+s[j]2-s[k]2)/(s[j]-s[k])<2s[i];
因為s[i]可看作常數,而左邊可抽象成(y1-y2)/(x1-x2)
即斜率,那麼只要滿足k與j之間斜率滿足此不等式,則就有j優於k
若不等號方向改變,則為k優於j
那麼求斜率的函式則為
int slope(int j,int k)
{
int x=(dp[j]-dp[k]+s[j]s[j]-s[k]s[k])/(s[j]+s[k]);
return x;
}
2.求得斜率後,便有三種情況(j,k,i)
一,三個點的兩條連線斜率均小於2s[i],則分別比較兩個斜率,斜率更大的右端點為最優點
二,三個點的兩條連線斜率均大於2s[i],則分別比較兩個斜率,斜率更大的左端點為最優點
三,三個點的兩條連線斜率一大一小,則可以判斷的是中間一個點一定不是最優點,可排除
3.排除完後,可發現這些點是成單調遞增趨勢的,將這些點放入佇列中
依次查詢符合要求的點
本題中最優點應為滿足斜率<2s[i]且位於最右邊的點
*/

相關文章