动态规划

有数组penny,penny中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim(小于等于1000)代表要找的钱数,求换钱有多少种方法。

给定数组penny及它的大小(小于等于50),同时给定一个整数aim,请返回有多少种方法可以凑成aim。

测试样例:
[1,2,4],3,3
返回:2
思路:这里dp[i][j]表示用前i种货币得到面值j的方法数。
import java.util.*;

public class Exchange {
    public int countWays(int[] penny, int n, int aim) {
        // write code here
        int[][] dp = new int[n][aim+1];
    	for(int i = 0;i < n; i++)
    		dp[i][0] = 1;
    	for(int i = 1;i <= aim; i++){
    		if(i%penny[0]==0)
    			dp[0][i] = 1;
    		else
    			dp[0][i] = 0;
    	}
    	for(int i = 1;i < n; i++){
    		for(int j = 1;j <= aim; j++){
    			int count = 0;
    			int k = 0;
    			while(j-k*penny[i]>=0){
    				count += dp[i-1][j-k*penny[i]];
    				k++;
    			}
    			dp[i][j] = count;
    		}
    	}
    	    		
    	return dp[n-1][aim];
    }
}

有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007

给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000。

测试样例:
1
返回:1
public class GoUpstairs {
    public int countWays(int n) {
        int f1 = 1;
        int f2 = 2;
        for(int i = 3; i <= n; ++i){
            int temp = (f1 + f2)%1000000007;
            f1 = f2;
            f2 = temp;
        }
        return f2 ;
    }
}

  

有一个矩阵map,它每个格子有一个权值。从左上角的格子开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。

给定一个矩阵map及它的行数n和列数m,请返回最小路径和。保证行列数均小于等于100.

测试样例:
[[1,2,3],[1,1,1]],2,3
返回:4
思路:这个地方dp[i][j]表示从(0,0)到(i,j)的最小路径和。
    public static int getMin(int[][] map, int n, int m) {
        // write code here
    	int[][] dp = new int[n][m];
    	int sum = 0;
    	for(int i = 0;i < m; i++){
    		dp[0][i] = sum+map[0][i];
    		sum = dp[0][i];
    		
    	}
    	
    	sum = 0;
    	for(int i = 0;i < n; i++){
    		dp[i][0] = sum+map[i][0];
    		sum = dp[i][0];
    	}
    	
    	for(int i = 1;i < n; i++)
    		for(int j = 1;j < m; j++)
    			dp[i][j] = Math.min(dp[i-1][j]+map[i][j], dp[i][j-1]+map[i][j]);
    	return dp[n-1][m-1];
    }

  

这是一个经典的LIS(即最长上升子序列)问题,请设计一个尽量优的解法求出序列的最长上升子序列的长度。

给定一个序列A及它的长度n(长度小于等于500),请返回LIS的长度。

测试样例:
[1,4,2,5,3],5
返回:3
思路:这个地方dp[i]表示以第i个数字结尾的最长递增子序列长度。
public static int getLIS(int[] A, int n) {
        // write code here
		int[] dp = new int[n];
		dp[0] = 1;
		for(int i = 1;i < n; i++)
			dp[i] = 0;
		int result = 0;
		for(int i = 1;i < n; i++){
			int max = 0;
			for(int j = i-1;j >= 0; j--)
				if(A[j]<A[i]&&dp[j]>max)
					max = dp[j];
				dp[i] = max+1;
			if(dp[i]>result)
				result = dp[i];
		}
			
		return result;
    }

  

一个背包有一定的承重cap,有N件物品,每件都有自己的价值,记录在数组v中,也都有自己的重量,记录在数组w中,每件物品只能选择要装入背包还是不装入背包,要求在不超过背包承重的前提下,选出物品的总价值最大。

给定物品的重量w价值v及物品数n和承重cap。请返回最大总价值。

测试样例:
[1,2,3],[1,2,3],3,6
返回:6
思路:这个地方dp[i][j]表示前i个物品在容量为j时所能达到的最大价值。
import java.util.*;

public class Backpack {
    public int maxValue(int[] w, int[] v, int n, int cap) {
        // write code here
            	int m = 0;
    	for(int i = 0;i < n; i++)
    		m += w[i];
    	//System.out.println(m);
    	int[][] dp = new int[n][m+1];
    	for(int i = 0;i < n; i++)
    		dp[i][0] = 0;
    	
    	for(int i = 0;i <= m; i++)
    		if(i>=w[0])
    			dp[0][i] = v[0];

    	
    	for(int i = 1;i < n; i++){
    		for(int j = 1;j <= m; j++){
    			if(j-w[i]>=0)
    				dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
    			else
    				dp[i][j] = dp[i-1][j];
    			//System.out.print(dp[i][j]+" ");
    		}
    		//System.out.println();
    	}
    		
    	if(cap>m)
    		return dp[n-1][m];
    	else
    		return dp[n-1][cap];
    }
}

  

对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价,请设计一个高效算法,求出将A串变为B串所需要的最少代价。

给定两个字符串AB,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。保证两串长度均小于等于300,且三种代价值均小于等于100。

测试样例:
"abc",3,"adc",3,5,3,100
返回:8
思路:dp[i][j]表示把A[0...i-1]字符串转换为B[0...j-1]的最小代价
import java.util.*;

public class MinCost {
    public int findMinCost(String A, int n, String B, int m, int c0, int c1, int c2) {
        // write code here
		int[][] dp = new int[A.length()+1][B.length()+1];
		for(int i = 0;i <= B.length(); i++)
			dp[0][i] = i*c0;
		for(int i = 1;i <= A.length(); i++)
			dp[i][0] = i*c1;
		for(int i = 1;i <= A.length(); i++)
			for(int j = 1;j <= B.length(); j++){
				int result = dp[i-1][j]+c1;			//删除
				int result2 = dp[i][j-1]+c0;		     //添加
				int result3 = 0;
				if(A.charAt(i-1)!=B.charAt(j-1))         //替换 
					result3 = dp[i-1][j-1]+c2;
				else
					result3 = dp[i-1][j-1];
				int min = result<result2?result:result2;
				min = min<result3?min:result3;
				dp[i][j] = min;
			}
//		for(int i = 0;i <= A.length(); i++){
//			for(int j = 0;j <= B.length(); j++)
//				System.out.print(dp[i][j]+" ");
//			System.out.println();
//		}
		return dp[A.length()][B.length()];
    }
}

  

给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。

给定两个字符串AB,同时给定两个串的长度nm,请返回最长公共子序列的长度。保证两串长度均小于等于300。

测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
思路:dp[i][j]代表A[0...i]和B[0...j]的最长子序列长度。
import java.util.*;

public class LCS {
    public int findLCS(String A, int n, String B, int m) {
        // write code here      
    	int[][] dp = new int[n][m];
    	boolean flag = false;
    	for(int i = 0;i < m; i++)
    		if(flag){
    			dp[0][i] = 1;
    		}else if(A.charAt(0)==B.charAt(i)){
    			flag = true;
    			dp[0][i] = 1;
    		}
    	flag = false;
    	for(int j = 0;j < n; j++)
    		if(flag){
    			dp[j][0] = 1;
    		}else if(A.charAt(j)==B.charAt(0)){
    			flag = true;
    			dp[j][0] = 1;
    		}
    	
    	for(int i = 1;i < n; i++)
    		for(int j = 1;j < m; j++){
    			int result = dp[i-1][j];
    			int result2 = dp[i][j-1];
    			int result3 = 0;
    			if(A.charAt(i)==B.charAt(j))
    				result3 = dp[i-1][j-1]+1;
    			int max = result>result2?result:result2;
    			max = max>result3?max:result3;
    			dp[i][j] = max;
    		}
//    	for(int i = 0;i < n; i++){
//    		for(int j = 0;j < m; j++)
//    			System.out.print(dp[i][j]+" ");
//    		System.out.println();
//    	}
    	return dp[n-1][m-1];
    }
}

  

原文地址:https://www.cnblogs.com/lxk2010012997/p/5545026.html