1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x51線性DP POJ3666分級

《演算法競賽進階指南》0x51線性DP POJ3666分級

題目連結:http://poj.org/problem?id=3666

題目給出一個序列a,要求給出一個序列b使得兩個數列每一項相減的絕對值之和最小,這裡有一個重要的性質:存在一個滿足條件的b,其中的數在a中都出現,可以通過數學歸納法去證明。

然後就是dp的轉移,前i個數設定好,並且第i個數是第j大的a中的數,這時的轉移方程是dp[i][j]=min{dp[i-1][k]}+abs(a[i]-a'[j]),其中k屬於[1,j]。

通過字首最大值的思想容易優化成O(n^2)

程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
using
namespace std; const int maxn = 2020; int a[maxn],b[maxn]; int f[maxn][maxn]; const int inf=0x7fffffff; int n; int dp(){ for(int i=1;i<=n;i++)b[i]=a[i]; sort(b+1,b+n+1); for(int i=1;i<=n;i++){ int minv=inf; for(int j=1;j<=n;j++){ minv=min(minv,f[i-1][j]); f[i][j]
=minv+abs(a[i]-b[j]); } } int res=inf; for(int i=1;i<=n;i++)res=min(res,f[n][i]); return res; } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; int res=dp(); reverse(a+1,a+n+1); res=min(res,dp()); cout<<res<<endl; }