动态规划之背包问题和区间模型--Java实现

背包问题描述:给定n个重量为w1,w2...wn、价值为v1,v2...vn的物品和一个承重量为W的背包,求这些物品中最优价值的一个子集,并且要能够装到背包中。

结论:1.在不包括第i个物品的子集中,最优子集的价值是Value[i-1][j].

2.在包括第i个物品的子集中(因此,j-wi>=0),最优子集是由该物品和前i-1个物品中能够放进承重量为j-wi的背包的最优子集组成。这种最优子集的总价值等于vi+Value[i-1][j-wi].

因此,在前i个物品中最优解的总价值等于这两个价值中的较大值。如果物品不能放进背包,从前i个物品中选出的最优子集的总价值等于从前i-1个物品中选出最优子集的总价值。递推式如下:

1)当j-wi<0,Value[i][j] = Value[i-1][j]

2)当j-wi>=0,Value[i][j] = max{Value[i-1][j] ,Value[i-1][j-wi]+vi}

代码如下:

package ExamTest;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by ZhangAnmy on 18/10/4.
 */
public class DPTest04 {
    public static void main(String[] args)
    {
        int weight = 5; //背包的承重量
        int number = 4; //物品个数
        int[] w = {0,2,1,3,2};//对应物品的重量,0表示没有此物品
        int[] v = {0,12,10,20,15};//对应物品的价值,0表示没有对应物品的价值

        int[][] Value = new int[5][6];
        beiBaoDP(Value,number,weight,w,v);
        System.out.println("The biggest total price is:"+Value[4][5]);

        bestSet(Value,number,weight,v);
    }

    public static void beiBaoDP(int[][] Value,int number,int weight,int[] w,int[] v)
    {
        for(int i=0;i<=number;i++)
        {
            for(int j=0;j<=weight;j++)
            {
                if(i == 0 || j == 0)
                {
                    Value[i][j] = 0;
                }
                else if(j-w[i]<0)
                {
                    Value[i][j] = Value[i-1][j];
                }
                else
                {
                    Value[i][j] = Math.max(Value[i-1][j],v[i]+Value[i-1][j-w[i]]);
                }

                System.out.print(Value[i][j]+" ");
            }
            System.out.println();
        }
    }

//回溯法找出对应最大价值的物品集合    
public static void bestSet(int[][] Value,int number,int weight,int[] v)
    {
        List<Integer> list = new ArrayList<Integer>();
        int value = Value[number][weight];
        for(int i=number;i>0;i--)
        {
            for(int j=weight;j>0;j--)
            {
                if(value == Value[i][j] && value != Value[i-1][j])
                {
                    list.add(i);
                    value = value - v[i];
                    break;
                }
            }
        }
        System.out.println("The set is: "+list);
    }
}

运行结果如下:

0 0  0  0   0  0
0 0  12 12 12 12
0 10 12 22 22 22
0 10 12 22 30 32
0 10 15 25 30 37
The biggest total price is:37
The set is: [4, 2, 1]

区间模型:状态表示一般为dp[i][j],表示区间[i, j]上的最优解,然后通过状态转移计算出[i+1, j]或者[i, j+1]上的最优解,并逐步扩大区间的范围,最终求得整个区间[1, len]的最优解。

问题描述:给定一个长度为n(n <= 1000)的字符串A,求插入最少多少个字符使得它变成一个回文串。

回文串拥有很明显的子结构特征,即当字符串X是一个回文串时,在X两边各添加一个字符’a’后,aXa仍然是一个回文串,用dp[i][j]来表示A[i…j]这个子串变成回文串所需要添加的最少的字符数。

那么对于A[i] == A[j]的情况,则有 dp[i][j] = dp[i+1][j-1] (即:判断A[i...j]中的子串A[i+1...j-1]。当i+1 > j-1时也是有意义的,它代表的是空串,空串也是一个回文串,所以这种情况下dp[i+1][j-1] = 0);

当A[i] != A[j]时,则将它变成更小的子问题求解,有两种决策:

1)在A[j]后面添加一个字符A[i];

2)在A[i]前面添加一个字符A[j];

根据两种决策列出状态转移方程为:

dp[i][j] = min{ dp[i+1][j], dp[i][j-1] } + 1; (每次状态转移,区间长度增加1)

空间复杂度O(n^2),时间复杂度O(n^2)。

补充:

  • 如果i == j,说明此时A中只有一个字符,其本身就是回文串,即:dp[i][j] = 0。
  • 如果A[i…j]有两个字符,如果两个字符相同则dp[i][j] = 0,否则dp[i][j] = 1。
  • 如果A[i…j]多于两个字母,如果A[i] == A[j]。则dp[i][j] = dp[i+1][j-1]。否则,dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1。举例说明,假设A = “abc”,可以先将“bc”变成回文串“bcb”(即:在后面添加1个字符),然后在末尾加“a”(即:A[i]),也可以先将“ab”变成回文串“aba”(即:在前面添加1个字符),然后在最前面加“c”(即:A[j])。即可以先处理A[i+1…j]然后末尾加A[i],也可以先处理A[[i…j-1],然后在开头加A[j]。

 代码如下:

package ExamTest;

import java.util.Scanner;
/**
 * Created by ZhangAnmy on 18/10/4.
 */
public class DPTestHuiWen {
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String[] strs = new String[n];

        for(int i=0;i<n;i++)
        {
            strs[i] = sc.next();
            numOfChars(strs[i]);
        }
    }

    public static void numOfChars(String str)
    {
        char[] strs = str.toCharArray();
        int num = strs.length;
        int dp[][]= new int[num][num];

        for (int i=num-1;i >= 0;i--)
        {
            for (int j=i+1;j<num;j++)
            {
                if (strs[i] == strs[j])
                {
                    dp[i][j]=dp[i+1][j-1];
                }
                else
                {
                    dp[i][j]=Math.min(dp[i+1][j],dp[i][j-1])+1;
                }
            }//end for
        }//end for
        System.out.println("The number of characters :" + dp[0][num-1]);
    }
}

运行结果:

3
a
The number of characters :0
abc
The number of characters :2
abc4cbe
The number of characters :2

猜你喜欢

转载自blog.csdn.net/m0_37568814/article/details/82938696