poj3666 Making the Grade(dp,离散化)

能采用动态规划求解的问题的一般要具有3个性质:

(1) 最优子结构:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

(2) 无后效性:有两层含义,第一层含义是,在推导后面阶段状态的时候,我们只关心前面阶段的状态值,不关心这个状态是怎么一步步推导出来的。第二层含义是,某阶段状态一旦确定,就不受之后阶段的决策影响。无后效性是一个非常“宽松”的要求。只要满足前面提到的动态规划问题模型,其实基本上都会满足无后效性。

(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

本题题意是,将一个无序的序列中的每个数进行加或减,整理为不上升或不下降序列,求所付出的最小代价。假设将序列整理为不下降序列,则思路为:
1,确定子问题,dp[i][j]表示前i个数的序列中,最大值(可理解为第i个值)为j的最小代价。则原问题为min(k){dp[n][k]},求前n个数中最大值为1~m的最小代价。
2,列出状态转移公式dp[i][j]=abs(j-w[i])+min(dp[i-1][k]);(k<=j)。
3,进行一些初始化工作。
这样分析,问题是满足以上三个条件的。动态规划可行。

优化方面,
1,序列最大值可达1e9,而序列长度仅几千,因此可以采取离散化来避免M和T。这里的离散化很简单,等于将序列排序。复杂的离散化请参考往期博文,这道题用不到。
2,min(dp[i-1][k])的确定无须在每个循环都遍历一遍dp[i-1]。只需在每个循环令mn=Math.min(mn, dp2[i-1][j]);这样,时间复杂度由O(nnm)下降为O(n*n)

ac代码


import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

class Main{
	public static void main(String args[]) throws IOException {		
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int a[]=new int[n],b[]=new int[n];
		int m=-1;
		for(int i=0;i<n;i++) {
			a[i]=sc.nextInt();
			b[i]=a[i];
		}
		Arrays.sort(b);

		int dp1[][]=new int[n][n];
		for(int i=0;i<n;i++) {
			int mn=Integer.MAX_VALUE;
			if(i==0)mn=0;
			for(int j=0;j<n;j++) {
				if(i-1>=0) {
					mn=Math.min(mn, dp1[i-1][j]);
				}		
				dp1[i][j]=mn+Math.abs(b[j]-a[i]);			
			}
		}
		int ans=Integer.MAX_VALUE;
		for(int i=0;i<n;i++) {
			ans=Math.min(dp1[n-1][i], ans);
		}
		

		int dp2[][]=new int[n][n];
		for(int i=0;i<n;i++) {
			int mn=Integer.MAX_VALUE;
			if(i==0)mn=0;
			for(int j=n-1;j>=0;j--) {
				if(i-1>=0) {
					mn=Math.min(mn, dp2[i-1][j]);
				}		
				dp2[i][j]=mn+Math.abs(b[j]-a[i]);			
			}
		}
		int ans2=Integer.MAX_VALUE;
		for(int i=0;i<n;i++) {
			ans2=Math.min(dp2[n-1][i], ans2);
		}
		//System.out.println(ans+" "+ans2);
		System.out.println(Math.min(ans, ans2));
	}



}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值