目录
【案例1 贪心尝试】
【题目描述】
【思路解析】
【代码实现】
【案例2 范围上的尝试模型】
【题目描述】
【思路解析】
【代码实现】
【案例3 范围上的尝试模型】
【题目描述】
【思路解析】
【代码实现】
【案例4 从左至右上尝试的模型 + 范围上的尝试模型】
【题目描述】
【思路解析】
【代码实现】
【案例5 范围上的尝试模型】
【题目描述】
【思路解析】
【代码实现】
先将整个数组排序,如果最后一个位置的值大于limit返回Integer.MAX_VALUE,表示不可能过岸。
arr[arr.length - 1] <= limit / 2如果满足这个条件,任意两个人都可以配对上一个船
arr[0] > limit / 2 如果满足这个条件,只能一个人上一个穿
否则找到小于等于limit/2最右的位置left,然后使用两个指针,p从left往0遍历,q从left+1往arr.lenth遍历。
(1)如果 p + q <= limit,此时q++ (这里q++时,统计q遍历的长度),直到p+q>limit。此时p左边的对应长度全部满足对应的p + q < limit。如果p左边长度不足,就将所有p的用来解决q。
(2)如果p+q > limit,p--。
遍历完成后,右边剩下的每个人单独安排一个船,左边剩下的安排(leftNum + 1)/2;解决多少个q就安排多少个船。
package AdvancedPromotion5;
import java.util.Arrays;
/**
* @ProjectName: study3
* @FileName: Ex1
* @author:HWJ
* @Data: 2023/9/24 16:18
*/
public class Ex1 {
public static void main(String[] args) {
int[] arr = {1,2,2,3,4,4,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,11,10,11,12};
System.out.println(need(arr, 13));
}
public static int need(int[] arr, int limit) {
Arrays.sort(arr);
if (arr[arr.length - 1] > limit) {
return Integer.MAX_VALUE;
}
if (arr[arr.length - 1] <= limit / 2) { // 如果满足这个条件,任意两个人都可以配对上一个船
return (arr.length + 1) / 2;
}
if (arr[0] > limit / 2) { // 如果满足这个条件,只能一个人上一个穿
return arr.length;
}
int left = -1;
for (int i = arr.length - 1; i >= 0; i--) {
if (arr[i] <= limit / 2) {
left = i;
break;
}
}
int p = left;
int q = left + 1;
int unSolved = 0;
while (p >= 0) {
int solve = 0;
while (q < arr.length && arr[p] + arr[q] <= limit) {
solve++;
q++;
}
if (solve > 0) {
p = Math.max(p - solve, -1); // 如果能解决完,就全部解决
// 不能就直接赋值-1,退出循环。
} else {
unSolved++;
p--;
// 我们只用记录左边没有解决的数量,就可以通过左边总数得到左边已经解决的数量
// 左边解决的数量,又等于右边解决的数量,通过这个又可以得到右边没有解决的数量。这样就得到了我们所需要的所有信息
}
}
int leftNum = left + 1;
int right = arr.length - leftNum;
return right + (unSolved + 1) / 2;
}
}
求公共子序列的做法看 案例6左神高阶进阶班4 (尼姆博弈问题、k伪进制、递归到动态规划、优先级结合的递归套路、子串的递归套路,子序列的递归套路,动态规划的压缩技巧)_Studying~的博客-CSDN博客
第一种做法,字符串str和它的逆序unStr求最长公共子序列则是他们的最长回文子序列。
第二种做法,给出一个定义dp[i][j]表示str【i 。。。j】上的最长回文子序列长度。
则最长回文子序列可能性包括:
(1)包含i位置,包含j位置,只有str[i] == str[j]
(2)包含i位置,不包含j位置
(3)不包含i位置,包含j位置
(4)不包含i位置,不包含j位置。
package AdvancedPromotion5;
/**
* @ProjectName: study3
* @FileName: Ex2
* @author:HWJ
* @Data: 2023/9/24 16:59
*/
public class Ex2 {
public static void main(String[] args) {
String str = "123akmk321";
System.out.println(getMax(str));
}
public static int getMax(String s){
char[] str = s.toCharArray();
int[][] map = new int[str.length][str.length];
for (int i = 0; i < str.length; i++) { // 填充i == j 和 j == i + 1的情况,这个是很好的判断,不用考虑可能性
map[i][i] = 1;
if (i < str.length - 1){
map[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
}
}
for (int i = str.length - 3; i >= 0; i--) {
for (int j = i + 2; j < str.length; j++) {
int p1 = map[i][j - 1];
int p2 = map[i + 1][j];
int p3 = map[i + 1][j - 1] + (str[i] == str[j] ? 2 : 0);
map[i][j] = Math.max(p1, Math.max(p2, p3));
}
}
return map[0][str.length - 1];
}
}
构造结果dp[i][j]表示把str[i.....j]变为回文串至少需要多少添加字符。
(1)str[i] != str[j],此时分为两种策略,先搞定str[i......j-1]让其变为回文串,然后再将次回文串的左侧填加一个str[j]则整体是一个回文串。或者先搞定str[i +1......j]让其变为回文串,然后再将次回文串的右侧填加一个str[i]则整体是一个回文串。
(2)str[i] == str[j],此时只用将str[i+1 ...... j-1]变为回文串,整体就是回文串。
最后三种可能性求最小值,则得到我们需要的答案。通过动态规划填完表后,我们通过表逆推得到返回的答案。
package AdvancedPromotion5;
/**
* @ProjectName: study3
* @FileName: Ex3
* @author:HWJ
* @Data: 2023/9/24 17:10
*/
public class Ex3 {
public static void main(String[] args) {
String s = "123afga1321";
System.out.println(record(s));
}
public static String record(String s) {
int[][] map = recordMap(s);
String ans = recordStr(map, s.toCharArray());
return ans;
}
public static int[][] recordMap(String s) {
char[] str = s.toCharArray();
int len = str.length;
int[][] map = new int[len][len];
for (int i = 0; i < len; i++) {
map[i][i] = 0;
if (i < len - 1) {
map[i][i + 1] = (str[i] == str[i + 1] ? 0 : 1);
}
}
for (int i = len - 3; i >= 0; i--) {
for (int j = i + 2; j < len; j++) {
int p1 = map[i + 1][j] + 1;
int p2 = map[i][j - 1] + 1;
map[i][j] = Math.min(p1, p2);
if (str[i] == str[j]) {
map[i][j] = Math.min(map[i][j], map[i + 1][j - 1]);
}
}
}
return map;
}
public static String recordStr(int[][] map, char[] str) {
int len = map.length;
int need = map[0][len - 1];
int p = 0;
int q = len - 1;
char[] ans = new char[len + need];
int i = 0;
int j = ans.length - 1;
while (p <= q) {
if (p == q) {
ans[i] = str[p];
break;
}
if (str[p] == str[q]) {
int p1 = map[p + 1][q - 1];
int p2 = map[p + 1][q];
if (map[p][q] == p1) {
ans[i++] = str[p++];
ans[j--] = str[q--];
} else if (map[p][q] == p2 + 1) {
ans[i++] = str[p];
ans[j--] = str[p++];
} else {
ans[i++] = str[q];
ans[j--] = str[q--];
}
} else {
int p1 = map[p + 1][q];
if (map[p][q] == p1 + 1) {
ans[i++] = str[p];
ans[j--] = str[p++];
} else {
ans[i++] = str[q];
ans[j--] = str[q--];
}
}
}
return String.valueOf(ans);
}
}
因为我们要将其全部切成回文子串的最小切割数,则可以从左至右遍历每种可能性,遍历可能性时判断当前切割是否合理,如果当前切割保证当前切下来的是合理回文子串,即继续往下切。这种遍历下时间复杂度O(N^2),又因为每次遍历都要进行一次判断过程,判断时间复杂度O(N),所以总体时间复杂度为O(N^3)。这时时间复杂度过高,我们需要通过范围上的模型来对判断过程加速。
统计一个表,map[i][j]表示str[i....j]是不是回文子串,回文子串一定要满足str[i] == str[j],并且str[i + 1][j - 1]也是回文子串。
package AdvancedPromotion5;
/**
* @ProjectName: study3
* @FileName: Ex4
* @author:HWJ
* @Data: 2023/9/25 8:32
*/
public class Ex4 {
public static void main(String[] args) {
String str = "ABA";
boolean[][] map = judge(str);
System.out.println(getMin(str.toCharArray(), 0, map));
}
public static int getMin(char[] str, int index, boolean[][] map){
if (index == str.length){
return -1;
}
int res = Integer.MAX_VALUE;
for (int i = index; i < str.length; i++) {
if (map[index][i]){
res = Math.min(res, 1 + getMin(str, i + 1, map));
}
}
return res;
}
public static boolean[][] judge(String s){
boolean[][] map = new boolean[s.length()][s.length()];
char[] str = s.toCharArray();
for (int k = 0; k < str.length; k++) {
map[k][k] = true;
if (k < str.length - 1){
map[k][k + 1] = str[k] == str[k + 1];
}
}
for (int k = str.length - 3; k >= 0 ; k--) {
for (int l = k + 2; l < str.length; l++) {
if (str[k] == str[l]){
map[k][l] = map[k + 1][l - 1];
}else {
map[k][l] = false;
}
}
}
return map;
}
}
构造一个二维表,map[i][j]表示str[i .....j]有多少个回文子序列。
则回文子序列分为以下可能性:
(1)包含i位置,包含j位置。
(2)包含i位置,不包含j位置。
(3)不包含i位置,包含j位置。
(4)不包含i位置,不包含j位置。
map[i + 1][j] 表示不包含i位置的所有回文子序列个数,可能包含j位置,也可能不包含j位置,代表(3)(4)可能性。j
map[i][j - 1] 表示不包含j位置的所有回文子序列个数,可能包含i位置,也可能不包含i位置,代表(2)(4)可能性。
map[i+1][j-1]表示不包含j位置并且不包含j位置的所有回文子序列个数,代表(4)可能性。
(1)可能性只有在str[i] == str[j]时才有这个可能性,数值等于map[i+1][j-1] + 1.因为如果如果除去这两个是回文数,加上任然是回文数,并且多一个只有这两个序列的情况。
package AdvancedPromotion5;
/**
* @ProjectName: study3
* @FileName: Ex5
* @author:HWJ
* @Data: 2023/9/25 9:05
*/
public class Ex5 {
public static void main(String[] args) {
String str= "ABA";
System.out.println(getNum(str));
}
public static int getNum(String s){
char[] str = s.toCharArray();
int len = str.length;
int[][] map = new int[len][len];
for (int i = 0; i < len; i++) {
map[i][i] = 1;
if (i < len - 1){
map[i][i + 1] = (str[i] == str[i + 1] ? 3 : 2);
}
}
for (int i = len - 3; i >= 0; i--) {
for (int j = i + 2; j < len; j++) {
map[i][j] = map[i + 1][j] + map[i][j - 1] - map[i + 1][j - 1];
if (str[i] == str[j]){
map[i][j] += (map[i + 1][j - 1] + 1);
}
}
}
return map[0][len - 1];
}
}