线性动态规划

线性动态规划

一、定义

    线性动态规划是指目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值。

二、典型例题

    1、最长上升序列问题

    问题描述:设有序列B为B1,B2,B3……Bn,若存在下标i1<i2<i3<……in,且Bi1<Bi2<Bi3<……Bin,则序列B中有长度为n的上升序列Bi1,Bi2,Bi3,……Bin。求给定一序列中的最长上升序列长度及该序列。

分析:

    ①设f(i)是前i个数中以Bi结尾的最长上升序列的长度,

则f(i)=max(f(j)+1),  (1<=j<i<=n,Bj<Bi),边界为f(1)=1;

    ②设t(i) 是前i个数中所有最长上升序列的个数,初始t(i)=1,如果f(i)=n时,将t(i)累加,(   1<=j<i<=n , Bi<Bj , f(i)=f(j)+1 )

例如

Bi

1

5

3

4

6

5

8

10

9

8

7

f(i)

1

2

2

3

4

4

5

6

6

5

5

T(i)

1

1

2

1

1

2

2

2

4

4

4

 

代码:

/*

*求最长上升子序列的元素的个数

*/

public class Long_list {

   public static void main(String[] args) {

      int s[]={1,5,3,4,6,5,8,10,9};

      int [] f=new int[s.length];

      int [] t=new int[s.length];

      f[0]=1;

      int maxLen=0;

      for (int i = 0; i < s.length; i++) {

         for (int j = 0; j < i; j++) {

            if(s[i]>s[j]&&f[j]+1>f[i])

                f[i]=f[j]+1;

         }

         if(maxLen<f[i])

            maxLen=f[i];

      }

      System.out.println(f[f.length-1]);

      Long_list l=new Long_list();

      //System.out.println(l.LIS_BSearch(s, t, s.length-1));

   }

}

 

//求最长上升子序列

 

public class LIS {

 

/*

 

 * 求一数列的最长上升子序列(元素都为非零元素)

 

 */

 

   public static void main(String[] args) {

 

      int []s ={1,2,9,4,6,7,4};

 

      //f数组来存储以其下标i(即s[i])结尾的最长上升序列元素的个数

 

      int []f = new int[s.length];

 

      //son数组来存储每个以s[i]结尾的最长上升子序列

 

      int [][]son = new int [s.length][s.length];

 

      f[0] = 1;

 

      int sum = 1;

 

      son[0][0] = s[0];

 

      for(int i=1;i<s.length;i++){

 

         for(int j=0;j<i;j++){

 

            if(s[i]>s[j]&&f[j]+1>f[i]){

 

                son[i][j] = s[j];

 

                f[i]=f[j]+1;

 

            }

 

         }

 

         son[i][i] = s[i];

 

         if(f[i]>sum) sum=f[i];

 

      }

 

      System.out.println("最长上升子序列的长度是 "+sum);

 

      int index = 0;

 

      int max = 0;

 

      for (int i = 0; i < son.length; i++) {

 

         int temp =0;

 

         for (int j = 0; j < son[0].length; j++) {

 

            if(son[i][j]!= 0) temp++;

 

         }

 

         if(temp>max){

 

            max = temp;

 

            index = i;

 

         }

 

      }

 

      System.out.print("最长上升子序列是:");

 

      for (int i = 0; i < s.length; i++) {

 

         if(son[index][i]!= 0)

 

         System.out.print(son[index][i]+" ");

 

      }

 

   }

 

}

 

    2、合唱队形

    问题描述:N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。   合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,  则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

分析:

首先用枚举法求出以每个人作为中间最高的同学是需要出列的同学,再在这些数据中求出最小值,即为答案。

如何求出出列的同学呢?即总人数 –留在队伍中的同学数,求留在队伍中的同学的即转换为了从左右两头求最长上升子序列问题。

代码:

public class Formation {//合唱队形问题

   public static void main(String[] args) {

      int []s={1,2,3,5,2,4,1};

      int []lenL=new int[s.length];

      int []lenR=new int[s.length];

      lenR[0]=1;

      lenL[0]=1;

      for (int i = 0; i < lenL.length; i++) {

         for (int j = 0; j < i; j++) {

            if(s[i]>s[j]&&lenL[i]<lenL[j]+1)

                lenL[i]=lenL[j]+1;

         }

      }

      for (int i = lenR.length-1; i >= 0; i--) {

         for (int j = lenR.length-1; j > i; j--) {

            if(s[i]>s[j]&&lenR[i]<lenR[j]+1)

                lenR[i]=lenR[j]+1;

         }

      }

      int len=0,x=0;

      for (int i = 0; i < lenR.length; i++) {

         if(len<lenR[i]+lenL[i]){

            len=lenR[i]+lenL[i];

            x=i;

         }

      }

      System.out.println("需要出列"+(s.length-len)+"人");

      System.out.println("队伍剩余人数是"+len+"人");

      System.out.println("中间的同学的高度是"+s[x]);

   }

}

3、数字串加“+”的最值问题

    问题描述:设一个由0到9是个数字组成的字符串,向该   字符串中加n个“+”,得到一个加法式,并计算得到结果。问如何添加这n个“+”使得最后的运算结果最小,输出该最小值。

分析:首先列出规划方程

F(i,j)=min[F(i-1,k)+Number(k,j)] (i<=k<j)

F(i,j)表示对于该字符串的前j个数字中添加i个加号后的最小值。

Number(i,j)表示对该字符串的第i个位置到第j个位置截取后对应的整数的值。

代码:

public class String_add_plus {

   //fun(str,i,j)表示对于str的前j个数字中添加i个加号后的最小值

   private static int fun(String str,int i,int j) {

      if(i==0){

         return Integer.parseInt(str.substring(0,j));

      }

      int min=Integer.parseInt(str);

      for(int k=i;k<j;k++){

         int temp=fun(str,i-1,k)+Integer.parseInt(str.substring(k,j));

         if(min>temp)

            min=temp;

      }

      return min;

   }

   //测试

   public static void main(String[] args) {

      String str="1234";

      int min=fun(str,2,str.length());

      System.out.println(min);

   } 

}

4、子集和问题(硬币问题)

    问题描述:设S={x1,x2,x3……xn}是一个正整数的集合,C是一个正整数,子集和问题就是判断是否存在S的一个子集S1,使得S1中元素的和为C。

    经典问题:给定11种面值分别为100元, 50元, 20元, 10元, and 5元 and 2元, 1元, 50分, 20分, 10分 and 5分的钱,现在给定一个钱数,求出可以组成的种类数。

    问题分析:

    设c(i,j)是a1,a2……ai中包含ai且数和为j的方案数,显然目标是求c(n,T)。将前i个正整数设为阶段(1<=i<=n),将k1*a1+k2*a2+…..+ki*ai的可能数和j(ai<=j<=T)设为状态,显然状态转移方程为c(i,j)=1(i=0)或者c(i,j)=c(k,j-ai){k=1到k=i-1}的和。

 

5、最长公共子序列问题(Longest Common Subsequence,LCS)

问题描述:

首先明确最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别。子串是串的一个连续的部分;子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列。也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。

分析:我们借助于一个二维棋盘来进行分析。

 

利用该棋盘逐步就可以求得一直到两个字符串的末尾时的最大子串的长度。

可以得到;

棋盘用数组s[i][j]表示,Xi,Yj表示字符串的第i、j个字符。

当i或j等于0时,s[i][j]=0;

状态转移方程是,

如果Xi==Yj,s[i][j]=max(s[i-1][j],s[i][j-1])+1;

如果Xi!=Yj,s[i][j]=max(s[i-1][j],s[i][j-1]);

代码;

public class LCS {

    private static int fun(String s1,String s2) {

       int[][]dp=new int[s1.length()+1][s2.length()+1];

       //初始化边缘部分

       for (int i = 0; i < dp.length; i++)

           dp[i][0]=0;

       for (int j = 0; j < dp[0].length; j++)

           dp[0][j]=0;

       //状态转移运算

       for (int i = 0; i < s1.length(); i++) {

           for (int j = 0; j < s2.length(); j++) {

              if(s1.charAt(i)==s2.charAt(j))

                  dp[i+1][j+1]=dp[i][j]+1;

              else dp[i+1][j+1]=dp[i+1][j]>dp[i][j+1]?dp[i+1][j]:dp[i][j+1];

           }

       }

       return dp[s1.length()][s2.length()];

    }

    //数据测试

    public static void main(String[] args) {

       String s1="123qwer",s2="123werwer";

       int num=fun(s1, s2);

       System.out.println(num);

    }

}

                                                                                                         --------亓慧杰                           

posted on 2016-11-13 17:35  JC向北  阅读(854)  评论(0编辑  收藏  举报

导航