1、切割钢条
一条N米长的钢条,不同长度价格不同,如下:
长度 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
价格 | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
可以用递归的方法解决,但是会消耗过多的时间的,因为当计算相同的一段长度时,时间还是双倍的,而不能借助之前已经计算出来的结果。
所以这是典型的时空权衡的例子。我们采用动态规划。
这里动态规划有两种,一种是从顶而下,第二种自底而上,这里介绍第二种。
public class Cutting_Bars {
int[] BOTTOM_UP_CUT_BAR(int[] p , int n){//传入钢条长度价格,实际拿到的钢条长度n
int r[] = new int[n+1];//初始化记忆dp,记录已经计算过的长度最优价格
r[0] = 0;//初始化
for(int i = 1 ; i <= n ; i ++){
int q = -1;
for(int j = 1 ; j <= i ; j ++){//查找i长度内最优切割价格
if (j >= p.length) {//判断j长度是否超出了价格表p的最大长度
q = max(q, p[p.length - 1] + r[i-(p.length - 1)]);
break;
}else {
q = max(q,p[j] + r[i-j]);//将i长度分两部分,j长度,i-j长度,j长度直接参考价格表,i-j长度参考最优r数组内的值
}
}
r[i] = q;
}
return r;
}
public static void main(String[] args) {
int[] p = {0,1,5,8,9,10,17,17,20,24,30};
Cutting_Bars cuttingBars = new Cutting_Bars();
int[] r = cuttingBars.BOTTOM_UP_CUT_BAR(p, 20);
System.out.println(r[20]);
}
private int max(int x , int y ){
if (x > y) {
return x;
}else {
return y;
}
}
}
2、最长递增子串
Longest_Increasing_Subsequence
输入 128 342 234 355 653 123 345
找出该串最长的递增子串的长度
输出:4(128 234 355 653或者 128 342 355 653)
public class Long_Increasing_Subsequence {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
int[] ints = new int[num];
scan.nextLine();
for(int i = 0 ; i < num ; i ++){
ints[i] = scan.nextInt();
}
int[] dp = new int[ints.length];//保存每个值对应最长递增子串长度
for(int i = 0 ; i < ints.length ; i ++){
dp[i] = 1;//默认自己既是符合条件的递增子串,长度为1
for(int j = 0 ; j < i ; j ++){//查找第i个值(包括i)之前值最长递增子串长度为多少,第i个值作为该最长子串的子串尾
if(ints[i] > ints[j] && dp[i] < dp[j] + 1){
dp[i] = dp[j] + 1;
}
}
}
int tmp = dp[0];
for(int i = 1 ; i < dp.length ; i ++){//输出dp内最长递增子串
if(tmp < dp[i])
tmp = dp[i];
}
System.out.println(tmp);
}
}
3、最长公共子串
Longest_Common_Subsequece
输入:A,B,C,B,D,A,B
B,D,C,A,B,A
查找该两串中,间隔的最长公共子串,即:按顺序从左到右双方都含有的最长子串
输出:B,C,B,A
三种情况:
X=<x1,x2,x3,x4,...,xm>,Y=<y1,y2,y3,y4,...,yn>,Z=<z1,z2,z3,z4,...,zk>为X和Y的任意LCS
1)xm = yn,则zk = xm=yn且Zk-1是Xm-1和Yn-1的一个LCS
2)xm<>yn,则zk<>xm意味着Z是Xm-1和Y的一个LCS
3)xm<>yn,则zk<>yn意味着Z是X和Yn-1的一个LCS
得到状态方程
c[][]表示Xi和Yj的LCS的长度
c[i][j] = 0 若i=0,或j=0
c[i][j] = c[i-1][j-1] + 1 若i,j>0且xi = yj
c[i][j] = max(c[i][j-1] , c[i-1][j]) 若i,j>0且xi <> yj
还构造了b[][]帮助构造最优解 0来自左上方,1来自上方,2来自左边
public class Longest_Common_Subsequence {
private int c[][];//保存
public int b[][];
int getLCS_length(char[] x ,char[] y){
int x_len = x.length;
int y_len = y.length;
c = new int[x_len+1][y_len+1];
b = new int[x_len+1][y_len+1];//0 左上方,1上方,2左方
for(int i = 0 ; i <= x_len ; i ++)
c[i][0] = 0;
for(int i = 0 ; i <= y_len ; i++)
c[0][i] = 0;
for(int i = 1 ; i <= x_len ; i ++){
for(int j = 1 ; j <= y_len ; j ++){
if (x[i-1] == y[j-1]) {
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = 0;//左上方
}else if(c[i][j-1] >= c[i-1][j]){
c[i][j] = c[i][j-1];
b[i][j] = 2;//上方
}else {
c[i][j] = c[i-1][j];
b[i][j] = 1;//左方
}
}
}
return c[x_len][y_len];
}
void print_lcs(int[][] b,char[] x,int x_len,int y_len){
if (x_len == 0 || y_len == 0) {
return;
}
if (b[x_len][y_len] == 0) {
print_lcs(b, x, x_len-1, y_len-1);
System.out.println(x[x_len-1]);
}else if (b[x_len][y_len] == 1) {
print_lcs(b, x, x_len-1, y_len);
}else {
print_lcs(b, x, x_len, y_len-1);
}
}
public static void main(String[] args) {
char[] x = {'a','b','c','b','d','a','b','b'};
char[] y = {'b','d','c','a','b','a','c'};
Longest_Common_Subsequence lcs = new Longest_Common_Subsequence();
System.out.println(lcs.getLCS_length(x, y));
lcs.print_lcs(lcs.b, x, x.length, y.length);
}
}
4、最长回文子串
Longest_Palindrome_Subsequece
何为回文?
输入:character
输出:carac
carac即为回文
这里分两种:1、连续回文最长子串;2、不连续回文最长子串
先说第二种:
不连续回文最长子串,其实就是把该子串倒一下,进行最长公共子串操作就能得出结果
第一种:
这里规定,单个字符肯定是回文子串
一个n长度的字符串,其中第i个字符到第j个字符之间为回文子串
那么,第i+1个字符到第j-1个字符肯定也是回文子串,以此类推,直到i+x=j-x的时候(即该回文子串为奇数个长度时)或者i+x+1=j-x并且第i+x个字符与第j-x个字符相同(即该回文子串为偶数个长度时)
public class Longest_Palindrome_Subsequence {
private int c[][];
public int b[][];
/**
* 连续的最长回文子串
* @param x
* @return
*/
char[] getLps(char[] x){
boolean[][] table = new boolean[1000][1000];
int n = x.length;
int longestBegin = 0;
int maxLen = 1;
for(int i = 0 ; i < n ; i ++)//初始化,单个字符都为回文子串
table[i][i] = true;
for(int i = 0 ; i < n - 1 ; i ++){//初始化,判断相邻的字符是否相等,即判断回文子串长度为2的子串有哪些
if (x[i] == x[i+1]) {
table[i][i+1] = true;
longestBegin = i;
maxLen = 2;
}
}
for(int len = 3 ; len <= n ; len ++){//理解其思想
for(int i = 0 ; i <= n-len ; i ++){//从长度为3的子串开始一个个判断,长度为2的子串之前已经都判断过了,现在在其基础上进行判断回文子串
int j = i + len - 1;
if(x[i] == x[j] && table[i+1][j-1]){
table[i][j] = true;
longestBegin = i;
maxLen = len;
}
}
}
char[] tmp = new char[maxLen];
for(int i = longestBegin ; i < longestBegin+maxLen ; i ++){
tmp[i-longestBegin] = x[i];
}
return tmp;
}
/**
* 获得不连续回文子串
* @param x
* @return
*/
void get_Incontinulity_Lps(char[] x){
int len = x.length;
char[] y = new char[len];
for(int i = x.length-1 ; i >= 0 ; i --)
y[x.length -1 -i] = x[i];
c = new int[len+1][len+1];
b = new int[len+1][len+1];
System.out.println(x);
System.out.println(y);
for(int i = 0 ; i <= len ; i ++){
c[i][0] = 0;
c[0][i] = 0;
}
for(int i = 1 ; i <= len ; i ++){
for(int j = 1 ; j <= len ; j ++){
if (x[i-1] == y[j -1]) {
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = 0;//左上方
}else if (c[i][j-1] >= c[i-1][j]) {
c[i][j] = c[i][j-1];
b[i][j] = 2;//左边
}else {
c[i][j] = c[i-1][j];
b[i][j] = 1;//上面
}
}
}
System.out.println(c[len][len]);
}
void print_lps(int[][] b,char[] x,int i , int j){
if (i == 0 || j == 0)
return;
if(b[i][j] == 0){
print_lps(b, x, i-1, j-1);
System.out.println(x[i -1]);
}else if (b[i][j] == 1) {
print_lps(b, x, i-1, j);
}else {
print_lps(b, x, i, j-1);
}
}
public static void main(String[] args) {
char[] str = {'c','h','a','r','a','c','t','e','r'};
Longest_Palindrome_Subsequence lps = new Longest_Palindrome_Subsequence();
// char[] tmp = lps.getLps(str);
// System.out.println(tmp);
lps.get_Incontinulity_Lps(str);
lps.print_lps(lps.b, str, str.length, str.length);
}
}