【BZOJ1003】[HNOI2008]玩具裝箱toy

1010: [HNOI2008]玩具裝箱toy

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3564  Solved: 1220
[Submit][Status][Discuss]

Description

P教授要去看奧運,但是他舍不下他的玩具,於是他決定把所有的玩具運到北京。他使用自己的壓縮器進行壓縮,其可以將任意物品變成一堆,再放到一種特殊的一維容器中。P教授有編號爲1...N的N件玩具,第i件玩具經過壓縮後變成一維長度爲Ci.爲了方便整理,P教授要求在一個一維容器中的玩具編號是連續的。同時如果一個一維容器中有多個玩具,那麼兩件玩具之間要加入一個單位長度的填充物,形式地說如果將第i件玩具到第j個玩具放到一個容器中,那麼容器的長度將爲 x=j-i+Sigma(Ck) i<=K<=j 製作容器的費用與容器的長度有關,根據教授研究,如果容器長度爲x,其製作費用爲(X-L)^2.其中L是一個 常量。P教授不關心容器的數目,他可以製作出任意長度的容器,甚至超過L。但他希望費用最小.

Input

第一行輸入兩個整數N,L.接下來N行輸入Ci.1<=N<=50000,1<=L,Ci<=10^7

Output

輸出最小費用

Sample Input

5 4
3
4
2
1
4

Sample Output

1


題解:

這一題學到好多, 首先是斜率優化DP, 然後是單調隊列維護。 其實這兩個知識很早以前我就看過了, 只是一直沒有什麼時間來學, 今天花了大約2個多小時細細的體會了一下, 豁然開朗的感覺。LOL!!


首先:

這道題一開始一定能想到一個O(N^2)的DP方程: 

dp[i] = dp[j] + min(sum[i] - sum[j] + i - j - 1 + L)^2

dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - 1 + L)^2)

接着化成斜率的形式什麼的,可以用筆推導一下, 過程雖然繁瑣, 但是很好想的:)


再講講寫率優化吧:

斜率優化,就是把決策的關係寫成(Y1-Y2)/(x1-x2)的斜率式,然後用隊列維護。

隊列中的決策需要保證兩個性質 : ① j1<j2<j3<j4....②slope(j1,j2)<slope(j2,j3)<slope(j3,j4)。


關於單調隊列:

1、對某個階段i進行決策時候,首先判斷用隊頭決策是否比用隊頭+1決策優,如果不優,就出隊。直到隊頭決策優。

2、計算階段i的值。

3、判斷 隊尾和i的斜率 與 隊尾-1和i的斜率 哪個大。如果 隊尾-1 大,那就將隊尾刪除。直到隊尾斜率大,然後i入隊。



代碼:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstring>
#define maxn 50050
using namespace std;
int d[maxn], head = 1, tail = 1, L, n;
long long dp[maxn], sum[maxn];
  
bool slope(int i, int j, int k)
{
    return ((dp[i] + sum[i] * sum[i]) - (dp[k] + sum[k] * sum[k])) * (sum[i] - sum[j]) < ((dp[i] + sum[i] * sum[i]) - (dp[j] + sum[j] * sum[j])) * (sum[i] - sum[k]);
}
  
long long cal(int i, int j)
{
    return dp[j] + (sum[i] - sum[j] - L) * (sum[i] - sum[j] - L);
}
  
void init()
{
    int i, j;
    scanf("%d%d", &n, &L);
    L++;
    for (i = 1; i <= n; i++)
    {
        scanf("%d", &j);
        sum[i] = sum[i - 1] + j + 1;
    }
}
  
void solve()
{
    int i;
    d[1] = 0;
    for (i = 1; i <= n; i++)
    {
        while (head < tail && cal(i, d[head]) > cal(i, d[head+1])) head++;
        dp[i] = cal(i, d[head]);
        while (head < tail && slope(i, d[tail - 1], d[tail])) tail--;
        d[++tail] = i;
    }
    printf("%lld\n", dp[n]);
}
  
int main()
{
    init();
    solve();
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章