1010: [HNOI2008]玩具裝箱toy
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3564 Solved: 1220
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
3
4
2
1
4
Sample Output
題解:
這一題學到好多, 首先是斜率優化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;
}