BZOJ 1010 [HNOI2008]玩具裝箱toy:斜率優化dp

Leohh發表於2018-02-02

題目連結:http://www.lydsy.com/JudgeOnline/problem.php?id=1010

題意:

  有n條線段,長度分別為C[i]。

  你需要將所有的線段分成若干組,每組中線段的編號必須連續。

  然後每組中的線段接成一排,若線段的編號為i to j,則總長度X = j - i + ∑ C[i to j]。

  對於每一個組,花費為(X - L)^2,其中L為給定常量。

  問你最小總花費。

 

題解:

  表示狀態:

    dp[i]表示已經將1 to i的線段分好組了,此時的最小總花費。

  找出答案:

    ans = dp[n]

  如何轉移:

    設s[i] = ∑ C[1 to i], L = L + 1.

    dp[i] = min dp[j] + (s[i]-s[j]- L)^2  (0 <= j < i)

  邊界條件:

    dp[0] = 0

  斜率優化:

    設j < k,且k的決策更優。

    則:dp[j] + (s[i]-s[j]- L)^2 > dp[k] + (s[i]-s[k]- L)^2

    整理得:(dp[k]+(s[k]+L)^2-dp[j]+(s[j]+L)^2) / (2*(s[k]-s[j])) < s[i]

    所以slope(i,j) = (dp[i]+(s[i]+L)^2-dp[j]+(s[j]+L)^2) / (2*(s[i]-s[j]))

    由於s[i]遞增,所以單調佇列維護下凸殼即可。

 

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX_N 50005
 5 
 6 using namespace std;
 7 
 8 int n,L;
 9 int q[MAX_N];
10 long long s[MAX_N];
11 long long dp[MAX_N];
12 
13 inline double slope(int i,int j)
14 {
15     return (dp[i]+(s[i]+L)*(s[i]+L)-dp[j]-(s[j]+L)*(s[j]+L))/(2.0*(s[i]-s[j]));
16 }
17 
18 int main()
19 {
20     cin>>n>>L; L++;
21     for(int i=1;i<=n;i++) cin>>s[i];
22     for(int i=2;i<=n;i++) s[i]+=s[i-1];
23     for(int i=1;i<=n;i++) s[i]+=i;
24     int l=1,r=1;
25     for(int i=1;i<=n;i++)
26     {
27         while(l<r && slope(q[l],q[l+1])<=s[i]) l++;
28         dp[i]=dp[q[l]]+(s[i]-s[q[l]]-L)*(s[i]-s[q[l]]-L);
29         while(l<r && slope(q[r],i)<slope(q[r-1],q[r])) r--;
30         q[++r]=i;
31     }
32     cout<<dp[n]<<endl;
33 }

 

相關文章