zoukankan      html  css  js  c++  java
  • GMOJ 1281旅行 题解

    warning:此方法可能较难理解 讲的烂

    约定

    1. 除去标有“移动”,所有的 (A) 都为原始输入顺序。

    2. (i ext{~}j) 的“代价”定义为 (sum_{k=i}^{j-1}|A_k-A_{k+1}|) .

    思路

    这种题比较明显的是DP,有两种解法。我果然选择了空间更差不能优化的顺推法

    首先,由于交换的位置从前往后,所以一个数要么向前移动1位,要么向后移动若干位(包括0)

    用不加调料的脑回路设状态:

    [F_{i,j}phantom{1}(ile j)phantom{11} ext{表示第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 ext{~}i) 的代价是固定的,可以以 (O(N)) 的时间复杂度计算。

    然后,对于移动后 (i ext{~}j) 的代价,因为只有 (A_i) 在移动,所以 (A_{i+1} ext{~}A_j) 的相对位置是不变的,于是移动后 (i+1 ext{~}j) 的代价当然也是不变的,可以预先用一个 (S) 数组 (O(n^2)) 预处理每个区间的代价。在此基础上,再加上 (|A_i-A_j|) ,就是移动后 (i ext{~}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 ext{~} 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} (ile 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);
    }
    
  • 相关阅读:
    java面向对象第六章
    java面向对象第四章
    java面向对象第三章
    java面向对象第二章
    java面向对象第一章
    java基础(9)
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    最小堆解决TOP N问题
    kafka相关
    golang 反射获取参数对应类型原始值
  • 原文地址:https://www.cnblogs.com/groundwater/p/13308211.html
Copyright © 2011-2022 走看看