1. 程式人生 > 實用技巧 >GMOJ 1281旅行 題解

GMOJ 1281旅行 題解

warning:此方法可能較難理解 講的爛

約定

  1. 除去標有“移動”,所有的 \(A\) 都為原始輸入順序。

  2. \(i\text{~}j\) 的“代價”定義為 \(\sum_{i}^{j}|A_i-A_j|\) .

思路

這種題比較明顯的是DP,有兩種解法。我果然選擇了空間更差不能優化的順推法

首先,由於交換的位置從前往後,所以一個數要麼向前移動1位,要麼向後移動若干位(包括0)

用不加調料的腦回路設狀態:

\[F_{i,j}\phantom{1}(i\le j)\phantom{11}\text{表示第i個位置的數向後移到了第j個位置,1~j的最小代價} \]

假設 \(A_i\) 向後移動,那麼 \(A_{i-1}\)

\(A_{i}\) 肯定沒有交換。明顯的,對於 \(i\) 固定的情況下,能轉移的前置 \(F\) 一定是固定的,為 \(F_{k,i-1}\phantom{1}(k<i)\) 。同時,因為第 \(i-1\) 個數(\(A_k\))和第 \(i\) 個數(注意是 \(A_{i+1}\)\(A_i\) 被換出去了)是確定的,所以移動後 \(1\text{~}i\) 的代價是固定的,可以以 \(O(N)\) 的時間複雜度計算。

然後,對於移動後 \(i\text{~}j\) 的代價,因為只有 \(A_i\) 在移動,所以 \(A_{i+1}\text{~}A_j\) 的相對位置是不變的,於是移動後 \(i+1\text{~}j\)

的代價當然也是不變的,可以預先用一個 \(S\) 陣列 \(O(n^2)\) 預處理每個區間的代價。在此基礎上,再加上 \(|A_i-A_j|\) ,就是移動後 \(i\text{~}j\) 的代價。

那麼,我們可以列出一條式子:

\[F_{i,j}=min{\{F_{k,i-1}+|A_{i+1}-A_k|\}}+S_{i+1,j}+|A_i-A_j|\phantom{11} (i<j,k<i) \]

\(S_{i,j}\) 表示原來中 \(i\text{~} j\)的代價)

對於 \(min\) 函式內的部分,就是之前提到可以預處理的部分。

我們要注意到這條式子有一個限制條件:\((i<j)\)

,因為我們假定 \(A_i\) 移動。

對於不移動的情況,我們只需要考慮前置 \(F\) 和結尾與 \(A_i\) 的差即可:

\[F_{i,i}=min{\{F_{k,i-1}+|A_i-A_k|\}}\phantom{11} (k<i) \]

(注意 \(min\) 函式裡面的差別)

那麼,最後的答案就是列舉一下誰在最後,即:

\[Ans=min{\{F_{i,n}\}}\phantom{11} (i\le n) \]

實現

注意對於 \(i=1\) 時,\(F_{i,i}\)\(min{\{F_{k,i-1}+|A_{i+1}-A_k|\}}\) 的值為0,由於不會被轉移,需要特殊處理。

程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,ans=0x7fffffff,a[2010],s[2010][2010],f[2010][2010];
void read(int &x){
	char c=getchar();
	for(;c<33;c=getchar());
	for(x=0;(c>47)&&(c<58);x=x*10+c-48,c=getchar());
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	for(int i=1;i<n;i++){
		for(int j=i+1;j<=n;j++){
			s[i][j]=s[i][j-1]+abs(a[j]-a[j-1]);
		}
	}
	for(int i=1;i<=n;i++){
		int temp=f[i][i]=i==1?0:0x7fffffff;
		for(int j=1;j<i;j++){
			temp=min(temp,f[j][i-1]+abs(a[i+1]-a[j]));
			f[i][i]=min(f[i][i],f[j][i-1]+abs(a[i]-a[j]));
		}
		for(int j=i+1;j<=n;j++){
			f[i][j]=temp+abs(a[i]-a[j])+s[i+1][j];
		}
	}
	for(int i=1;i<=n;i++){
		ans=min(ans,f[i][n]);
	}
	printf("%d",ans);
}