用一題來說明dp平行四邊優化
阿新 • • 發佈:2019-02-14
poj 1160
題意:給出n個村莊,村莊是一條直線排好的,並且給出每個村莊到直線最左邊初始點的距離。現在要建立m個郵局,於是引入一個距離S{各個村莊到最近的郵局的距離和}。那麼現在問題來了如何使得這個S的值最小?
題解:定義這樣的方程 dp[i][j] = min{ dp[i-1][k] + w[k+1][j] } 我們可以看出這個方程滿足這樣的模型 s[i][j] = s[i][k] + s[k+1][j] 那麼我們可以斷定用平行四邊形優化。那麼定義mark[i][j]表示最大的k就可以把複雜度降到O(n^2)
注意一點,在迴圈的第二層是倒著推的,為什麼?很顯然如果正著推的話會產生後效性,因為我們加入了mark[i][j]這個輔助的原因,如果沒有這個輔助可以正的推,加了之後因為mark[i][n+1]=n,如果正著推發現mark[i][j]都沒計算出來,顯然dp過程就錯了,倒著推很明顯mark[i][n+1]是計算出來的,這樣遞推下去才不會有沒計算的。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<map> using namespace std; //typedef __int64 lld; #define oo 0x3f3f3f3f #define maxn 305 #define maxm 35 int dp[maxn][maxm]; int mark[maxn][maxm]; int w[maxn][maxn],val[maxn]; int main() { int n,m; while(scanf("%d %d",&n,&m)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) w[i][j]=w[i][j-1]+val[j]-val[(i+j)/2]; } memset(dp,0x3f,sizeof dp); for(int i=1;i<=n;i++) { dp[1][i]=w[1][i]; mark[1][i]=0; } for(int i=2;i<=m;i++) { mark[i][n+1]=n;//倒著推,防止後效性 for(int j=n;j>=i;j--) for(int k=mark[i-1][j];k<=mark[i][j+1];k++) if(dp[i][j]>dp[i-1][k]+w[k+1][j]) { dp[i][j]=dp[i-1][k]+w[k+1][j]; mark[i][j]=k; } } printf("%d\n",dp[m][n]); } return 0; }