1. 程式人生 > >HDU - 2829 Lawrence [ 斜率優化dp ]

HDU - 2829 Lawrence [ 斜率優化dp ]

題目連結 : HDU - 2829

題意 :

給出一條筆直無分叉的鐵路上有n個倉庫,每個倉庫有一個v[i]代表價值;

每兩個倉庫之間算作一段鐵路,現在有m次攻擊機會,一次攻擊可以炸燬一段鐵路;

m次攻擊後,剩餘的總價值為:Σ(v[i]*v[j]),i和j為所有任意兩個互相可到達的倉庫。

現要求選定m段鐵路進行攻擊炸燬,然後使得總價值最小。

思路 :

正常的思路,首先想到用滾動陣列,用dp[i][j] 代表 以 j 結尾的分成了 i 段的最小值,那

d

p [ i ] [ j ] = m i
n d p [ i 1 ] [
k ] + w [ j ] w [ k ] s u m [ k ] ( s u m [ j ] s u m [ k ] )
dp[i][j] = min { dp[i-1][k] + w[j] - w[k] - sum[k] * (sum[j] - sum[k]) } 但是對於這種情況 ,

我們要 遍歷一遍 i j k 因此時間複雜度是O( n ^ 3 ) 而題目的資料是1000,因此要用的斜

率優化,如果對於 q 來說 有一個更加優化的 k 的話那麼

d p [ i 1 ] [ k ] + w [ j ] w [ k ] s u m [ k ] ( s u m [ j ] s u m [ k ] ) < d p [ i 1 ] [ q ] + w [ j ] w [ q ] s u m [ q ] ( s u m [ j ] s u m [ q ] ) dp[i-1][k] + w[j] - w[k] - sum[k] * (sum[j] - sum[k]) < dp[i-1][q] + w[j] - w[q] - sum[q] * (sum[j] - sum[q])

化簡得 :

( d p [ i 1 ] [ k ] w [ k ] + s u m [ k ] s u m [ k ] ( d p [ i 1 ] [ q ] w [ q ] + s u m [ q ] s u m [ q ] ) ) / ( s u m [ k ) s u m [ q ] ) < s u m [ j ] (dp[i-1][k] - w[k] + sum[k] * sum[k] - ( dp[i-1][q] - w[q] + sum[q] * sum[q] )) / (sum[k) - sum[q]) < sum[j]

AC code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

const int maxn = 1e3+50;

ll dp[maxn][maxn] ,w[maxn] ,sum[maxn] ,v[maxn] ;
int que[maxn] ,head ,tail ;
int n ,m ;

ll y (int i,int j,int k) {
	return dp[i][k] - w[k] + sum[k] * sum[k] - (dp[i][j] - w[j] + sum[j] * sum[j]);
}

ll x (int j,int k) {
	return sum[k] - sum[j];
}

int main() {
	while(~scanf("%d %d",&n ,&m ) &&(n+m)) {
		w[0] = sum[0] = 0;
		for (int i = 1;i<=n;i++) {
			scanf("%lld",&v[i]);
			sum[i] = sum[i-1] + v[i];
			w[i] = w[i-1] + sum[i-1] * v[i];
		}
		for (int i = 1;i <= n;i++) {
			dp[1][i] = w[i];
		}
		for (int i = 2;i <= m+1;i++) {
			head = tail = 0;
			que[tail ++] = i-1;
			for (int j = i;j<=n;j++) {
				while(head + 1 < tail && y(i-1 ,que[head] ,que[head + 1]) < x(que[head] ,que[head + 1]) * sum[j] ) head ++;
				dp[i][j] = dp[i-1][que[head]] + w[j] - w[que[head]] - sum[que[head]] * ( sum[j] - sum[que[head]] ); 
				while(head + 1 < tail && y(i-1 ,que[tail-2] ,que[tail-1]) * x(que[tail-1] ,j ) >= y(i-1 ,que[tail - 1] ,j) * x(que[tail-2] ,que[tail-1]) ) tail -- ;
				que[tail ++] = j;
			}
		}
		printf("%lld\n",dp[m+1][n]);
	}
	return 0;
}