1. 程式人生 > 實用技巧 >Codeforces 704B. Ant Man 題解

Codeforces 704B. Ant Man 題解

題目大意:

  • 給定\(n\)個元素,每一個元素有\(x_i,a_i,b_i,c_i,d_i\)
  • 求一個\(1\)~\(n\)的全排列\(p_1,p_2,\dots,p_n\)使得其價值最小,其中\(p_1=s,p_n=e\)
  • 定義一個排列的價值為\(\sum_{i=1}^{n-1}f(p_i,p_{i+1})\)
  • 函式\(f(i,j)\)定義為\(f(i,j)=\left \{\begin{array}{}x_i-x_j+c_i+b_j(i>j)\\x_j-x_i+d_i+a_j(i<j) \end{array}\right.\)
  • 求所有滿足條件排列的最小价值。

題目連結:

704B. Ant Man


題解:在原題中,很容易考慮到將\(a_i\)變為\(a_i-x_i\)\(b_i,c_i,d_i\)以此類推,所以就可以消除\(x_i\),僅考慮\(a_i,b_i,c_i,d_i\)對答案的影響。

考慮 DP。設\(f_{i,j}\)表示從小到大選擇了前\(i\)個數,一共分成了\(j\)個連續段的最小价值,然後對於每加入一個數,考慮它自己單獨作為一段,連在一個段左端,連在一個段右端,連在兩個段中間四種情況來考慮。對於起點和終點,只需要分兩個段考慮即可。轉移方程自己在草稿紙上畫一下應該就能出來。

但是這一道題有一個比較麻煩的地方,就是起點不能往左合併,終點不能往右合併,這一種情況要考慮全,然後在轉移中通過特判刪掉這種轉移方法即可。

時間複雜度\(o(n^2)\)

據說還有更加快速的貪心演算法,但是我沒有考慮了。

下面是程式碼。

#include <cstdio>
#include <cstring>
template<typename Elem>
Elem min(Elem a,Elem b){
	return a<b?a:b;
}
typedef long long ll;
const int Maxn=5000;
const ll Inf=0x3f3f3f3f3f3f3f3fll;
ll f[Maxn+5][Maxn+5];
int n,s,e;
int x[Maxn+5],a[Maxn+5],b[Maxn+5],c[Maxn+5],d[Maxn+5];
int main(){
	scanf("%d%d%d",&n,&s,&e);
	for(int i=1;i<=n;i++){
		scanf("%d",&x[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&d[i]);
	}
	for(int i=1;i<=n;i++){
		a[i]+=x[i];
		c[i]+=x[i];
		b[i]-=x[i];
		d[i]-=x[i];
	}
	memset(f,0x3f,sizeof f);
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		if(i!=s&&i!=e){
			for(int j=1;j<=i;j++){
				f[i][j]=min(f[i][j],f[i-1][j-1]+b[i]+d[i]);
				if(j>(i>e)||i==n){
					f[i][j]=min(f[i][j],f[i-1][j]+a[i]+d[i]);
				}
				if(j>(i>s)||i==n){
					f[i][j]=min(f[i][j],f[i-1][j]+b[i]+c[i]);
				}
				if(j+1>(i>e)+(i>s)||i==n){
					f[i][j]=min(f[i][j],f[i-1][j+1]+a[i]+c[i]);
				}
			}
		}
		else if(i==s){
			for(int j=1;j<=i;j++){
				if(j>(i>e)||i==n){
					f[i][j]=min(f[i][j],f[i-1][j-1]+d[i]);
					f[i][j]=min(f[i][j],f[i-1][j]+c[i]);
				}
			}
		}
		else{
			for(int j=1;j<=i;j++){
				if(j>(i>s)||i==n){
					f[i][j]=min(f[i][j],f[i-1][j-1]+b[i]);
					f[i][j]=min(f[i][j],f[i-1][j]+a[i]);
				}
			}
		}
	}
	printf("%lld\n",f[n][1]);
	return 0;
}