求数组中不相邻的最大值
解决方案,假设opt数组为最优解,比如opt[6]就表示arr数组中下标0到6这段的最优解
即opt[n]=Math.max(opt[n-1],opt[n-2]+arr[n])
上诉公式表示 不取下标为n的选项和取下标为n的选项两种方案的最大值
边界为 opt[0]=arr.get(0) opt[1]=Math.max(arr.get(0),arr.get(1)),还是比较好理解的。
/**
* 获取数组arr中不相邻的数字相加最大值
* @param arr
* @return
*/
private static Integer getMaxValue(List<Integer> arr){
Integer[] opt=new Integer[arr.size()];
opt[0] = arr.get(0);
opt[1] = Math.max(arr.get(0), arr.get(1));
for(int i=2;i<arr.size();i++){
Integer a = arr.get(i) + opt[i - 2];
Integer b = opt[i - 1];
opt[i] = Math.max(a, b);
}
System.out.println(JSON.toJSONString(opt));
return opt[arr.size() - 1];
}
求数组中是否存在不相邻的选项和为某值
递归方案:
/**
* 递归方式求list数组中,是否存在不相邻的数字和为result
* @param list
* @param result
* @return
*/
private static boolean containSubset(List<Integer> list,Integer length,Integer result){
if(result==0)return true;
if(length.equals(1)){
return list.get(0).equals(result);
}else if(length.equals(2)){
return list.get(0).equals(result) || list.get(1).equals(result);
}
if(list.get(length-1)>result){
//不选
return containSubset(list, length - 1, result);
}
boolean A = containSubset(list, length - 2, result - list.get(length - 1));
boolean B = containSubset(list, length - 1, result);
return A || B;
}
动态规划方案:
/**
* 动态规划 求list数组中不相邻的数字和是否可以为result()
* @param list
* @param result
* @return
*/
private static boolean dp_subset(List<Integer> list,Integer result){
Boolean[][] arr = new Boolean[list.size()][result + 1];
for(int i=0;i<list.size();i++){
arr[i][0] = true;
}
for(int j=1;j<=result;j++){
arr[0][j] = list.get(0).equals(j);
}
for(int j=1;j<=result;j++){
arr[1][j] = arr[0][j] || list.get(1).equals(j);
}
for(int i=2;i<list.size();i++){
for(int j=1;j<result+1;j++){
if(list.get(i)>j){
arr[i][j] = arr[i - 1][j];
}else{
boolean A = arr[i - 1][j];
boolean B = arr[i - 2][j - list.get(i)];
arr[i][j] = A || B;
}
}
}
//showArr(arr);
return arr[list.size() - 1][result];
}
动态规划方案不太好理解,这里举例
list为5, 4, 3, 1, 6, 2, 7
,result为12
0 1 2 3 4 5 6 7 8 9 10 11 12
5 true false false false false true false false false false false false false
4 true false false false true true false false false false false false false
3 true false false true true true false false true false false false false
1 true true false true true true true false true false false false false
6 true true false true true true true false true true true true false
2 true true true true true true true true true true true true false
7 true true true true true true true true true true true true true
纵坐标为list数组中的具体值,横坐标为result
每个坐标的意思就是在子数组中是否存在不相邻的和为坐标值的组合
比如arr[2,4]表示5,4,3三个子集中是否存在不相邻的组合和为4
可以明星看出最右下角的二维数组值就是要的结果。
国王与金矿(0-1背包问题)
这两个问题都差不多
/**
*
* @param n 第几个金矿
* @param w 总共有几个人
* @param g 数组,存放每个金矿的黄金数
* @param p 数组,存放每个金矿需要的工人数
* @return
*/
public static int getMostGlodForDP2(int n, int w, int[] g, int[] p) {
int[][] arr=new int[n][w+1];
for(int i=0;i<n;i++){
for(int j=0;j<w+1;j++){
//j即为人数
//第一座金矿
if(i==0){
if(j<p[0]){
arr[i][j]=0;
}else{
arr[i][j] = g[i];
}
}else{
if(j<p[i]){
arr[i][j] = arr[i - 1][j];
}else{
//不采这个金矿收益
int a=arr[i-1][j];
//采集这个金矿收益(金矿收益+剩余人数的最大收益)
int b = g[i] + arr[i - 1][j - p[i]];
arr[i][j] = Math.max(a, b);
}
}
}
}
showArr(arr);
return arr[n - 1][w - 1];
}
/**
* 打印二维数组
* @param arr
*/
private static void showArr(int[][] arr){
for(int i=0;i<arr.length;i++){
System.out.println("");
for(int j=0;j<arr[i].length;j++){
System.out.print(StringUtils.center(String.valueOf(arr[i][j]), 5, " "));
}
}
}
输出:
int[] g = { 400, 500, 200, 300, 350 };
int[] p = { 5, 5, 3, 4, 3 };
getMostGlodForDP2(5, 10, g, p);
0 0 0 0 0 400 400 400 400 400 400
0 0 0 0 0 500 500 500 500 500 900
0 0 0 200 200 500 500 500 700 700 900
0 0 0 200 300 500 500 500 700 800 900
0 0 0 350 350 500 550 650 850 850 900
横向为人数(横向第一列表示人数0,1个人,2个人,3个人。。。),纵向为金矿
可以一目了然的看出每添加一座金矿,10个人的收益情况
上诉代码最难理解的就是这块int b = g[i] + arr[i - 1][j - p[i]];
代码的意思就是,采集这块金矿收益+剩余人数采集其他金矿的最大收益
其实这里只要其他金矿收益,只要两个一维数组就可以了,这里方便
最长回文子串
/**
* 动态规划方式
* @param s
* @return
*/
private static String myTestCode(String s){
int len = s.length();
if(len<2)return s;
char[] arr = s.toCharArray();
int maxLen=1;
int begin=0;
boolean[][] dp = new boolean[len][len];
for(int j=1;j<len;j++){
for(int i=0;i<j;i++){
if(arr[i]!=arr[j]){
dp[i][j]=false;
}else{
if(j-i>2){
dp[i][j] = dp[i + 1][j - 1];
}else{
dp[i][j]=true;
}
}
if(dp[i][j]&&(j-i+1)>maxLen){
begin=i;
maxLen = j - i + 1;
}
}
}
return s.substring(begin, begin + maxLen);
}
参考
算法-动态规划 Dynamic Programming--从菜鸟到老鸟