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;
}
posted @ 2020-07-27 16:17  with_hope  阅读(129)  评论(0编辑  收藏  举报