• 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 {egin{array}{}x_i-x_j+c_i+b_j(i>j)\x_j-x_i+d_i+a_j(i<j) end{array} ight.)
    • 求所有满足条件排列的最小价值。

    题目链接: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
    ",f[n][1]);
    	return 0;
    }
    
  • 相关阅读:
    16061109-第0次个人作业
    面向对象第四次总结
    面向对象5-7次作业总结
    2018 OO第一次总结(作业1-3)
    (最终作业)面向对象先导课课程总结
    HTML学习笔记
    实验八 进程间通信
    信号
    进程基础
    shell脚本编程
  • 原文地址:https://www.cnblogs.com/withhope/p/13386023.html
Copyright © 2020-2023  润新知