- int[] d,d[i]:i号怪兽的能力
int[] p,p[i]:i号怪兽要求的钱
开始时你的能力是0,你的目标是从0号怪兽开始,通过所有的怪兽。
如果你当前的能力,小于i号怪兽的能力,你必须付出p[i]的钱,贿赂这个怪兽,然后怪兽就会加入你,他的能力直接累加到你的能力上;如果你当前的能力,大于等于i号怪兽的能力,你可以选择直接通过,你的能力并不会下降,你也可以选择贿赂这个怪兽,然后怪兽就会加入你,他的能力直接累加到你的能力上。
返回通过所有的怪兽,需要花的最小钱数。
/**
* int[] d,d[i]:i号怪兽的能力
* int[] p,p[i]:i号怪兽要求的钱
* 开始时你的能力是0,你的目标是从0号怪兽开始,通过所有的怪兽。
* 如果你当前的能力,小于i号怪兽的能力,你必须付出p[i]的钱,贿赂这个怪兽,
* 然后怪兽就会加入你,他的能力直接累加到你的能力上;如果你当前的能力,大于等于i号怪兽的能力,
* 你可以选择直接通过,你的能力并不会下降,你也可以选择贿赂这个怪兽,然后怪兽就会加入你,他的能力直接累加到你的能力上。
* 返回通过所有的怪兽,需要花的最小钱数。
*/
public class MinMoney {
// 暴力递归
public static int minMoney(int[] d,int[] p){
if(d == null || p == null){
return 0;
}
return process(d,p,0,0);
}
// 含义:应经通过了0 ~ index - 1的怪兽,此时的能力是able,index及其往后的的怪兽最少需要多少钱才可以全部通过
private static int process(int[] d,int[] p,int index,int able){
if(index == d.length){
// 如果已经没有怪兽了,那么花费为0
return 0;
}
if(able < d[index]){
// 此时的能力小于需要通过怪兽的能力,
// 则只能花钱通过,且获得怪兽的能力
return process(d,p,index+1,able+d[index])+p[index];
}else{
// 能力够,则可花钱,可不花钱
int p1 = process(d,p,index+1,able);
int p2 = process(d,p,index+1,able+d[index]) + p[index];
return Math.min(p1,p2);
}
}
// 动态规划
public static long dp(int[] d,int[] p){
int len1 = d.length;
int len2 = p.length;
int a = 0;
for (int i = 0; i < d.length; i++) {
a+=d[i];
}
long[][] dp = new long[len1+1][a+1];
// dp[i][j],此时来到i号怪兽,能力为j,需要的钱,
// dp[len1+1][i] = 0;
for (int i = 0; i <= a; i++) {
dp[0][i] = 0;
}
for(int i = dp.length -2;i>=0;i--){
for (int j = 0; j <= dp[0].length; j++) {
if (j + d[i] > a) {
continue;
}
if(j < d[i]){
// 此时的能力小于需要通过怪兽的能力,
// 则只能花钱通过,且获得怪兽的能力
dp[i][j] = dp[i+1][j+d[i]]+p[i];
}else{
// 能力够,则可花钱,可不花钱
long p1 = dp[i+1][j];
long p2 = dp[i+1][j+d[i]]+ p[i];
dp[i][j] = Math.min(p1,p2);
}
}
}
return dp[0][0];
}
}
1.给定一个字符串,如果只能在后面添加字符,最少添加几个能让字符串整体都是回文串。
2.给定一个字符串,如果可随意添加字符,最少添加几个能让字符串整体都是回文串。
/**
* 给定一个字符串,如果可随意添加字符,最少添加几个能让字符串整体都是回文串。
*/
// 范围上尝试模型
public class MinAddCharNum {
public static int minAddCharNum(String str){
if(str == null){
return 0;
}
// 定义dp数组,dp[i][j] 表示从i位置开始,j位置结束的子串要变成回文串需要添
// 几个字符
// i的取值范围 0~str.length(),j的取值范围0~str.length()
char[] c = str.toCharArray();
int[][] dp = new int[c.length][c.length];
//范围上尝试的模型i<=j,除了对角线以外,左下部分都是无效区域
// 对角线表示i = j时,也就是子串只有一个字符的时候需要添的字符数为0
// 对角线的上一条斜线表示两个字符时需要添加的字符数,两个字符相等则是0,不等则是1
for (int i = 0;i < c.length-1;i++){
dp[i][i+1] = c[i] == c[i+1] ? 0 : 1;
}
// 普遍位置i,j
// 1,c[i] = c[j] ,也就是i,j位置字符相等,那么就看i+1到j-1子串需要添加几个字符变为回文串
// 2,c[i] != c[j],此时又可分为两种
// 1),i位置先不管,看i+1~j的子串需要添加几个字符,最后在加上和i位置配对的1个
// 2),j位置先不管,看i~j-1的子串需要添加几个字符,最后在加上和j位置配对的1个
// 1),2)中选最小的
for(int j = 2;j < c.length;j++){
for (int i = j-2; i >= 0; i--) {
if(c[i] == c[j]){
dp[i][j] = dp[i+1][j-1];
}else{
int p1 = dp[i+1][j] + 1;
int p2 = dp[i][j-1] + 1;
dp[i][j] = Math.min(p1,p2);
}
}
}
return dp[0][c.length-1];
}
public static void main(String[] args) {
String str = "AB1CD2EFG3H43IJK2L1MN";
System.out.println(minAddCharNum(str));
}
}
public static String getPalindrome1(String str) {
if (str == null || str.length() < 2) {
return str;
}
char[] chas = str.toCharArray();
int[][] dp = getDP(chas);
char[] res = new char[chas.length + dp[0][chas.length - 1]];
int i = 0;
int j = chas.length - 1;
int resl = 0;
int resr = res.length - 1;
while (i <= j) {
if (chas[i] == chas[j]) {
res[resl++] = chas[i++];
res[resr--] = chas[j--];
} else if (dp[i][j - 1] < dp[i + 1][j]) {
res[resl++] = chas[j];
res[resr--] = chas[j--];
} else {
res[resl++] = chas[i];
res[resr--] = chas[i++];
}
}
return String.valueOf(res);
}
- 一种消息接收并打印的结构设计,已知一个消息流会不断地吐出整数 1~N,但不一定按照顺序吐出。如果上次打印的数为 i, 那么当 i+1 出现时,请打印 i+1 及其之后接收过的并且连续的所有数,直到 1~N 全部接收 并打印完,请设计这种接收并打印的结构。初始时默认i==0
/**
* 一种消息接收并打印的结构设计
* 已知一个消息流会不断地吐出整数 1~N,但不一定按照顺序吐出。如果上次打印的数为 i,
* 那么当 i+1 出现时,请打印 i+1 及其之后接收过的并且连续的所有数,直到 1~N 全部接收
* 并打印完,请设计这种接收并打印的结构。初始时默认i==0
*/
public class PrintNum {
static class Node{
int no;
String info;
Node next;
public Node(int no,String info){
this.no = no;
this.info = info;
}
}
static HashMap head;
static HashMap tail;
static int curPrintIndex;
public PrintNum(){
this.head = new HashMap<>();
this.tail = new HashMap<>();
this.curPrintIndex = 1;
}
public static void printNum(int no,String info){
Node node = new Node(no,info);
// 当有消息进来获取他是几号消息,加入头表,和尾表
head.put(node.no,node);
tail.put(node.no,node);
// 当前信息的前一个序号是否在尾表中
if(tail.containsKey(node.no-1)){
// 如果在围标中,把他从围标中删除
Node pre = tail.get(node.no - 1);
tail.remove(node.no-1);
head.remove(node.no);
pre.next = node;
}
// 在头表中是否有当前节点后一个节点
if(head.containsKey(node.no+1)){
// 如果有跟新头表中的头为当前节点
Node next = head.get(node.no + 1);
node.next = next;
tail.remove(next.no);
head.remove(next.no);
}
// 如果当前来到的节点刚好是要打印的序列则打印
if(node.no == curPrintIndex){
print(node);
System.out.println(" ");
}
}
private static void print(Node node){
// 打印节点并在头尾表中删除
Node cur = node;
while (cur != null){
System.out.print(cur.info + " ");
cur = cur.next;
curPrintIndex ++;
}
head.remove(node.no);
tail.remove(curPrintIndex -1);
}
}
- 现有n1+n2种面值的硬币,其中前n1种为普通币,可以取任意枚,后n2种为纪念币,
每种最多只能取一枚,每种硬币有一个面值,问能用多少种方法拼出m的面值?
/**
* 现有n1+n2种面值的硬币,其中前n1种为普通币,可以取任意枚,后n2种为纪念币,
* 每种最多只能取一枚,每种硬币有一个面值,问能用多少种方法拼出m的面值?
*/
public class MoneyNum {
public static int moneyNum(int[] arr1,int[] arr2,int m){
if(arr1 == null && arr2 == null){
return 0;
}
// 对普通币求动态规划
int len1 = arr1.length;
int[][] dp1 = new int[len1][m+1];
// dp[i][j] 表示0~号位置的货币随意选,刚好筹够j元的方法数
// 第一列,货币随便选,凑够0元的方法数
for (int i = 0; i < len1; i++) {
// 凑出0元都只有一种方法,那就是什么都选
dp1[i][0] = 1;
}
// 第一行
for (int i = 0; i <= m; i++) {
dp1[0][i] = i % arr1[0] == 0 ? 1 : 0;
}
for (int i = 0; i < len1; i++) {
for (int j = 0; j <= m; j++) {
dp1[i][j] = dp1[i-1][j] + (j - arr1[i] >= 0 ? dp1[i][j-arr1[i]] : 0);
}
}
int len2 = arr2.length;
// 纪念币
int[][] dp2 = new int[len1][m+1];
for (int i = 0; i < len2; i++) {
// 凑出0元都只有一种方法,那就是什么都选
dp2[i][0] = 1;
}
for (int i = 0; i <= m; i++) {
dp2[0][i] = arr1[0]== m ? 1 : 0;
}
for (int i = 0; i < len2; i++) {
for (int j = 0; j <= m; j++) {
dp2[i][j] = dp1[i-1][j] + (j-arr2[i] >= 0 ?dp2[i-1][j-arr2[i]] : 0);
}
}
int res = 0;
for (int i = 0; i <= m; i++) {
res += dp1[len1-1][i] * dp2[len2-1][m - i];
}
return res;
}
public static void main(String[] args) {
}
}
- 给定一个正数N,表示你在纸上写下1~N所有的数字,返回在书写的过程中,一共写下了多少个1
- 先给出可整合数组的定义:如果一个数组在排序之后,每相邻两个数差的绝对值 都为 1, 则该数组为可整合数组。例如,[5,3,4,6,2]排序之后为[2,3,4,5,6], 符合每相邻两个数差的绝对值 都为 1,所以这个数组为可整合数组。 给定一个整型数组 arr,请返回其中最大可整合子数组的长度。例如, [5,5,3,2,6,4,3]的最大 可整合子数组为[5,3,2,6,4],所以返回 5。
/**
* 先给出可整合数组的定义:如果一个数组在排序之后,每相邻两个数差的绝对值 都为 1,
* 则该数组为可整合数组。例如,[5,3,4,6,2]排序之后为[2,3,4,5,6],
* 符合每相邻两个数差的绝对值 都为 1,所以这个数组为可整合数组。 给定一个整型数组 arr,
* 请返回其中最大可整合子数组的长度。例如, [5,5,3,2,6,4,3]的最大 可整合子数组为[5,3,2,6,4],所以返回 5。
*/
public class KeZhengHeShu {
public static int getMaxLen(int[] arr){
if(arr == null || arr.length == 0){
return 0;
}
// 题中可整合数的定义: 1.排好序后,依次递增,且增量为1.
// 暴力方法枚举所有数组(O(n^2)),排序子数组(O(nlogn)) -> O(n^3logn)
// 重新定可整合数: 1.子数组中没有重复值,2.子数组中最大值减最小值等于元素个数减1
// 最大的可整合数
int res = 0;
Set set = new HashSet<>();
// 枚举所有的子数组
for (int i = 0; i < arr.length; i++) {
set.clear();
set.add(arr[i]);
int max = arr[0];
int min = arr[0];
for (int j = i+1; j < arr.length; j++) {
if(set.contains(arr[j])){
// 换头
break;
}
set.add(arr[j]);
min = Math.min(arr[j],min);
max = Math.max(arr[j],max);
if(max - min == set.size()-1){
res = Math.max(res,set.size());
}
}
}
return res;
}
public static void main(String[] args) {
int[] arr = { 5, 5, 3, 2, 6, 4, 3 };
System.out.println(getMaxLen(arr));
}
}