DAY20

题目一

一个字符串 最少分隔几个部分 让它全都是回文字符串

做一个dp[][]数组  dp[i][j] 表示i....j位置上的字符串是否为回文字符串 依赖于dp[i+1][j-1] 也就是左边界缩一个格子 右边界缩一个格子 

basecase 对角线 dp[i][i]只有一个字符 必为回文

DAY20_第1张图片

 最后一列 倒数第二行的格子 也就是对角线右边的一条线 依赖左下的话 是空的 所以一次填两条斜线万无一失

第二条斜线也好办 就两个元素 相等为回文 不等为非回文

然后填好每个格子 除了一条斜线一条斜线的填 我们也可以从下往上填 每一行的开始点都是行+2 因为本来是行=列开始的 从图中我们也可以看出 每行都填好了两个格子

额 这样我们就生成好了一张表 来看每一个区域是不是回文

 

然后在来一个一维的dp数组

dp[i]位置表示 i...结尾位置最少分隔几个成为回文字符串

然后我们枚举i 就是i为开头 然后在每个i中 枚举每个结尾 就相当于枚举每个子数组了 不过为啥不枚举结尾呢 一定要从后往前枚举开头呢

dp[i]是i...结尾位置最少分隔几个成为回文字符串的话 dp1[end+1]是 这个结尾的

DAY20_第2张图片

这个MAX_VaLUE这一步很关键 因为最开始都是0 没填过的格子会被min识别成有效且最小的

 当i...end为回文的时候 我们就要考虑 要不要切这么一刀 当前位置的分隔次数肯定是 它分隔前的次数+1刀 因为是吧i...end这个区间作为一个新的区间 但是呢 这一刀也不一定要

比如说 abakfk     i==5 的时候 

dp[5] MAX和0+1 哪个更小 dp[5] = 1

i==4的时候

从end==4开始结算  MAX和 1+1 选择2 这种每次都是+1的选择是最坏的可能性 我上一个格子的分隔区间 直接加一(我自身独立成一个格子) 接下来的每种走法 都是看能不能和之前的格子连接上 形成回文了 如果可以形成回文 也不一定是当前区间最长就是最小的 可能是 5长度 1 长度 1长度 1长度 四个区间 而4长度 三长度 两个区间才是最优解 所以要遍历每一个可能的区间 然后往回推 看看到底算上当前区间要分几个区间

如果是分割次数 那就是分割区间减一喽

public int  minCut(String s) {
		 if (s == null || s.length() == 0) {
			return 0;
		}
		if (s.length() == 1) {
			return 0;
		}
      char [] chars = s.toCharArray();
      int N = chars.length;
      boolean [][] dp = new boolean [N][N];
      for(int i = 0;i= 0; row--) {
			for (int col = row + 2; col < N; col++) {//开始的两列填完了
				dp[row][col] = chars[row] == chars[col] && dp[row + 1][col - 1];
			}
		}
      int[] dp1 = new int[N + 1];
		for (int i = 0; i <= N; i++) {
			dp1[i] = Integer.MAX_VALUE;
		}
		dp1[N] = 0;
		for (int i = N - 1; i >= 0; i--) {
			for (int end = i; end < N; end++) {
				// i..end
				if (dp[i][end]) {
					dp1[i] = Math.min(dp1[i], 1 + dp1[end + 1]);
				}
			}
		}
		return dp1[0]-1;
	}

 

力扣链接-力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

 

 

题目二 

给定两个有序数组arr1和arr2,再给定一个正数K
求两个数累加和最大的前K个,两个数必须分别来自arr1和arr2 可以重复使用数 但是同一个组合只能使用一次
 

额 做一张二维表 里面有所有数的累加和 dp[i][j]为 arr1 i位置+arr2 j位置的和 

然后再有一张同样格式的表

然后做一个大根堆 大根堆最大容量为K 先把右下角的放进去 右下角一定是最大的 然后看这个值的上面和左面哪个值大 把它放入堆中 因为它是一个有序数组 两个数组都取最大的值 肯定是最大的 左侧和上侧 分别代表 arr1取倒数第二个和arr2取倒数第一个  arr1取倒数第一个和arr2取倒数第二个 看看他俩哪个大 然后把它放在堆中 然后再看它的左侧和上侧 wrong 两个都放进去 如果超额的放了 那么 它是一个大根堆 会把大的放在前面 然后只取较大的

记得不要重复加入 like this

DAY20_第3张图片 

这个D点就是重复加入了 by the way 这个B点 A点 一定分别大于CD和ED 这个倒是没问题 但是当C大于E 且只要五个元素的情况呢 我们会先加入最开始的星 弹出星 +B +A 然后弹出B +E +D 然后弹出A +D +C 每次加入两个元素 我担心的 还没加入本层元素 就收集完毕的情况不存在 收集元素到第N层的时候 外面那层已经加入完毕了并参与排序了  假如说我们A B为第二层 我们收集元素肯定是在第二层 在第二层收集完成之前 不会进入下一层 而当前层收集一遍的时候 就把所有下一层元素加进去了

 public static int [] TopKsum(int [] arr1,int [] arr2,int K) {
if (arr1 == null || arr2 == null || K < 1) {
			return null;
		}
		// arr1 50  arr2 20   1000   topk 100万
		K = Math.min(K, arr1.length * arr2.length);
	 int length1 = arr1.length;
	 int length2 = arr2.length;
     int cur1 = length1-1;
     int cur2 = length2-1;
     PriorityQueue stack = new PriorityQueue(new mycompare());
     stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
     int count = 0;
     int [] res = new int [K];
     boolean [][] isin = new boolean[arr1.length][arr2.length];
     while(count<=K) {
    	 /*res[count] = stack.poll().value;
    	 count++;
    	 if(cur1-1>=0&&isin[cur1-1][cur2]==false) {
    		 cur1--;
    		 stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
    		 isin[cur1][cur2] = true;
    	 }
    	 if(cur2-1>=0&&isin[cur1][cur2-1]==false) {
    		 cur2--;
    		 stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
    		 isin[cur1][cur2] = true;  这么做不对 逻辑就错了 每次只能走两个 只针对第一次循环是正确的
    	 }*/
    	 Node node = stack.poll();
    	 res[count] = node.value;
    	 count++;
    	 cur1 = node.i1;
    	 cur2 = node.i2;
    	 if(cur1-1>=0&&isin[cur1-1][cur2]==false) {
    		 cur1--;
    		 stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
    		 isin[cur1][cur2] = true;
    	 }
    	 if(cur2-1>=0&&isin[cur1][cur2-1]==false) {
    		 cur2--;
    		 stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
    		 isin[cur1][cur2] = true;
    	 
     }
     }
     return res;
     
	}

记得洗一下参数 如果拢共笛卡尔积都没有K那么多 那就不用找那么多

第三题

给定一个正数数组arr,返回该数组能不能分成4个部分,并且每个部分的累加
和相等,切分位置的数不要。
例如:
arr=[3, 2,4, 1,4,9, 5, 10,1, 2, 2]返回true三个切割点下标为2, 5, 7.切出的四个子数组为[3,2]. [1,4]. [5], [1,2,2],累加和都是5

首先 如果它长度不大于等于七 就是白给

然后我们的思路是枚举每一刀的位置

首先第一刀 1....N-6上面选 再后面就凑不齐三刀了

假设我们第一刀的位置为a 那么a的前缀和为x 

那么第二刀的位置它的前缀和一定是2x+a

第三刀的前缀和一定是 3x+a+b

然后剩下的 也得是x

那就得做个累加和预处理数组了 然后遍历每个位置

或者我们主动出击 做个hashmap 看看需要多少累加和 那就直接找到

 public static boolean canSplits(int[] arr) {
		 if (arr.length<7) {
			return false;
		}
	HashMap map = new HashMap();
	int sum = arr[0];
	for (int i = 1; i < arr.length; i++) {
		map.put(sum, i);
		sum += arr[i];
	}
	int lsum = arr[0]; // 第一刀左侧的累加和 就是x拉
	for (int s1 = 1; s1 < arr.length - 5; s1++) { // s1是第一刀的位置
		int checkSum = lsum * 2 + arr[s1]; // 100 x 100   100*2 + x
		if (map.containsKey(checkSum)) {
			int s2 = map.get(checkSum); // j -> y
			checkSum += lsum + arr[s2];
			if (map.containsKey(checkSum)) { // 100 * 3 + x + y
				int s3 = map.get(checkSum); // k -> z
				if (checkSum + arr[s3] + lsum == sum) {
					return true;
				}
			}
		}
		lsum += arr[s1];
	}
	return false;
     
	}

你可能感兴趣的:(算法)