- 每种工作有难度和报酬,规定如下
class Job {
public int money;// 该工作的报酬
public int hard; // 该工作的难度
}
给定一个Job类型的数组jobarr,表示所有岗位,每个岗位都可以提供任意份工作
选工作的标准是在难度不超过自身能力值的情况下,选择报酬最高的岗位
给定一个int类型的数组arr,表示所有人的能力
返回int类型的数组,表示每个人按照标准选工作后所能获得的最高报酬 - 先按照难度排序从小到大,难度相同的按照报酬从大到小,每个难度里面只选报酬最大的,组成一个数组,然后在新的数组中如果难度增大了,但是报酬没有增大,那么淘汰掉这个job,最后又会形成一个新数组,一定是相同难度下报酬最高的,且难度增大报酬必增大的数组,然后组成大跟堆,看能力数组能不能匹配上。
public class MaxSlary {
public static int[] maxSlary(Job[] job,int[] ables){
if(job == null || job == null || ables == null || ables.length == 0){
return null;
}
// 先按照难度从小到大排序相同难度,报酬从大到小排序
Arrays.sort(job,new MyHardC());
TreeMap map = new TreeMap<>();
// 每个相同难度都选择一个报酬最大的进有序表
Job preHard = job[0];
map.put(preHard.hard,preHard.money);
for(Job item : job){
if(preHard.hard != item.hard && item.money > preHard.money){
map.put(item.hard,item.money);
preHard = item;
}
}
// 遍历数组
int[] res = new int[ables.length];
for (int i = 0; i < ables.length; i++) {
if(map.floorKey(ables[i]) != null){
res[i] = map.get(map.floorKey(ables[i]));
}else {
res[i] = 0;
}
}
return res;
}
static class MyHardC implements Comparator{
@Override
public int compare(Job o1, Job o2) {
return o1.hard != o2.hard ?o1.hard - o2.hard:o2.money -o1.money ;
}
}
public static void main(String[] args) {
Job[] jobs = new Job[]{new Job(1,2),new Job(1,3),new Job(2,2),new Job(2,4),new Job(3,5),new Job(5,3)};
int[] ables = new int[]{1,1,2,3,4,5,6,7,8};
int[] res = maxSlary(jobs, ables);
for (int i : res){
System.out.print(i+ " ");
}
}
}
- 背包容量为w,一共有n袋零食, 第i袋零食体积为v[i]>0 ,总体积不超过背包容量的情况下,一共有多少种零食放法?(总体积为0也算一种放法)。
- 从左到右的尝试模型,先写暴力接,再改动态规划。
/**
* 背包容量为w
* 一共有n袋零食, 第i袋零食体积为v[i]
* 总体积不超过背包容量的情况下,
* 一共有多少种零食放法?(总体积为0也算一种放法)。
*/
public class BagFood {
public static int bagFood1(int[] v,int w){
return process(v,0,w);
}
// 从index开始任意选零食,剩下的背包容量为rest,不超过容量的前提下有几种选法
private static int process(int[] v,int index,int rest){
if(rest < 0){
// 如果已经没有剩余背包空间了,那么说明之前的选法有问题,可行方法数为0
return 0;
}
// 到这里说明背包还有剩余空间
// 如果此时已经没有零食了,说明前面的方案可行有一种方案
int len = v.length;
if(index == len){
return 1;
}
// 背包既还有空间,零食也没有选完
// 当前步骤可以选择要零食,和不要零食两种情况
// 累加两种情况的数量
// 不要零食
int no = process(v,index+1,rest);
int yes = process(v,index+1,rest-v[index]);
return no+yes;
}
// 动态规划
public static int dp(int[] v,int w){
int row = v.length;
int[][] dp = new int[row+1][w+1];
int n = dp.length;
// rest >=0 时 最后一行全是1,表示背包还有空间时,零食已经选完了达标
for(int i = 0;i < dp[0].length;i++){
dp[n-1][i] = 1;
}
for (int i = dp.length-2; i >= 0; i--) {
for (int j = 0; j < dp[0].length; j++) {
// 最后一行开始填
dp[i][j] = dp[i+1][j]+ (j-v[i] >=0 ? dp[i+1][j-v[i]] : 0);
}
}
return dp[0][w];
}
private static int[] getArr(int size,int max){
int[] res = new int[(int)(Math.random()*size)+1];
for (int i = 0; i < res.length; i++) {
// Math.random() -> [0,1)
res[i] =(int)(Math.random()*max);
}
return res;
}
private static int[] copyArr(int[] arr){
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
public static void main(String[] args) {
int size = 100;
int max = 9999;
System.out.println("statr");
for (int i = 0; i < 10; i++) {
int[] a = getArr(size, max);
int[] c = copyArr(a);
int w = (int)Math.random()*max;
int i1 = bagFood1(a, w);
int dp = dp(c, w);
if(i1 != dp){
System.out.println("error!");
}
}
System.out.println("end");
}
}
- 给定一个二维数组matrix,其中每个数都是正数,要求从左上到右下
每一步只能向右或者向下,沿途经过的数字要累加起来,最后请返回最小的路径和。动态规划的空间压缩技巧!
/**
* 给定一个二维数组matrix,其中每个数都是正数,要求从左上到右下
* 每一步只能向右或者向下,沿途经过的数字要累加起来
* 最后请返回最小的路径和
* 动态规划的空间压缩技巧!
*/
public class MinPath {
// 暴力递归
public static int minPath(int[][] matrix){
if(matrix == null || matrix.length == 0){
return 0;
}
return process(matrix,0,0);
}
// 当前来到了(i,j)位置,之前的路径已经求过了,求后续从(i,j)位置触发的最小路径和
private static int process(int[][] matrix,int i,int j){
// 如果已经到了终点,那么返回收集的答案
int endi = matrix.length - 1;
int endj = matrix[0].length - 1;
if(i > endi || j > endj){
return 0;
}
if(i == endi && j == endj){
return matrix[i][j];
}
// 有两种选择,向下或者向右
//1.向下
int dowm = process(matrix, i + 1, j);
//2.向右
int right = process(matrix, i, j + 1);
// 求两个中最小的
// 如果返回0代表没有路径不收集答案
if(dowm == 0 && right == 0){
return 0;
}
dowm = dowm == 0 ? Integer.MAX_VALUE:dowm;
right = right == 0 ? Integer.MAX_VALUE:right;
return Math.min(dowm,right)+matrix[i][j];
}
public static int dp(int[][] matrix){
if(matrix == null || matrix.length == 0){
return 0;
}
int[][] dp = new int[matrix.length][matrix[0].length];
int r = dp.length;
int c = dp[0].length;
// 从最后一行开始填
for (int i = r - 1; i >= 0; i--) {
for(int j = c-1;j >= 0;j--){
int dowm = i+1 < r ? dp[i + 1][j] :0;
int right = j+1 < c ?dp[i][j + 1]: 0 ;
if(dowm == 0&&right==0){
// 右下角终点
dp[i][j] = matrix[i][j];
continue;
}
dowm = dowm == 0 ? Integer.MAX_VALUE:dowm;
right = right == 0 ? Integer.MAX_VALUE:right;
dp[i][j] = Math.min(dowm,right)+matrix[i][j];
}
}
return dp[0][0];
}
// 另一种尝试
// matrix[i][j]表示从起点开始到i,j点的最短路径
// 由题可知,每个点只依赖他的左边个点,和上面个点
private static int dp2(int[][] matrix){
if(matrix == null || matrix.length == 0){
return 0;
}
int r = matrix.length;
int c = matrix[0].length;
int[][] dp = new int[r][c];
// 第一列
for (int k = 0; k < r; k++) {
dp[k][0] += k-1>=0 ? matrix[k-1][0]+matrix[k][0] : matrix[k][0];
}
// 第一行
for (int k = 0; k < c; k++) {
dp[0][k] = k-1>=0 ? matrix[0][k-1]+matrix[0][k]:matrix[0][k];
}
for (int k = 1; k < r; k++) {
for (int l = 1; l < c; l++) {
dp[k][l] = Math.min(dp[k-1][l],dp[k][l-1]) + matrix[k][l];
}
}
return dp[r-1][c-1];
}
public static int dp3(int[][] matrix){
if(matrix == null || matrix.length == 0){
return 0;
}
int r = matrix.length;
int c = matrix[0].length;
int[] dp = new int[c];
// 先填第一行
// 第一行
for (int k = 0; k < c; k++) {
dp[k] = k-1>=0 ? matrix[0][k-1]+matrix[0][k]:matrix[0][k];
}
for (int i = 1; i < r; i++) {
dp[0] += matrix[i][0];
for (int j = 1; j < c; j++) {
// 上
int temp = dp[j];
// 左 dp[j-1]
dp[j] = Math.min(temp,dp[j-1]) + matrix[i][j];
}
}
return dp[c-1];
}
public static void main(String[] args) {
int[][]matrix = new int[][]{
{1,2,3,4,5},
{1,1,1,1,2},
{1,1,2,2,2},
{4,1,2,8,1}
};
System.out.println(minPath(matrix));
System.out.println(dp(matrix));
System.out.println(dp2(matrix));
System.out.println(dp3(matrix));
}
}
- 请注意区分子串和子序列的不同,给定两个字符串str1和str2,求两个字符的最长公共子序列
动态规划的空间压缩技巧! - 一个样本作行,一个样本作列的尝试模型。
/**
* 请注意区分子串和子序列的不同,给定两个字符串str1和str2,
* 求两个字符的最长公共子序列
* 动态规划的空间压缩技巧!
*/
public class MaxSubSequence {
// 暴力递归解
public static String maxSubSequence(String str1,String str2){
if(str1 == null || str1.equals("") || str1 == null || str2.equals("")){
return "";
}
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int len1 = c1.length;
int len2 = c2.length;
int[][] dp = new int[len1][len2];
// dp[i][j] 表示,str1以结尾,str2以j结尾他们的最长子序列的长度是多少
// 第一行
for (int i = 0; i < len2; i++) {
dp[0][i] = c2[i] == c1[0] ? 1 : dp[0][i-1];
}
// 第一列
for (int i = 0; i < len1; i++) {
dp[i][0] = c1[i] == c2[0] ? 1 : dp[i-1][0];
}
for (int i = 1; i < len1; i++) {
for (int j = 1; j < len2; j++) {
// 判断两个字符串的最长公共子序列是否已ij结尾
int p1 = 0;
int p2 = 0;
int p3 = 0;
int p4 = 0;
if(c1[i] == c2[j]){
p1 = dp[i-1][j-1] + 1;
}else if(c1[i-1] == c2[j]){
p2 = dp[i-1][j];
}else if(c1[i] == c2[j-1]){
p3 = dp[i][j-1];
}else {
p4 = dp[i-1][j-1];
}
dp[i][j] = Math.max(p1,Math.max(p2,Math.max(p3,p4)));
}
}
int maxSubSeq = dp[len1-1][len2-1];
char[] resC = new char[maxSubSeq];
int index = maxSubSeq - 1;
len1 = len1 - 1;
len2 = len2 - 1;
while (index >= 0){
// 把结果数组填好
if(len2 >0 && dp[len1][len2] == dp[len1][len2-1]){
len2 --;
}else if(len1 > 0&& dp[len1][len2] == dp[len1-1][len2]){
len1 --;
}else{
resC[index--] = c1[len1];
len1--;
len2--;
}
}
return String.valueOf(resC);
}
public static void main(String[] args) {
String str1 = "amghnbcni";
String str2 = "atlgnbfvi";
System.out.println(maxSubSequence(str1,str2));
}
}
- 最长公共子串
/**
* 请注意区分子串和子序列的不同,
* 给定两个字符串str1和str2,求两个字符串的最长公共子串
* 动态规划的空间压缩技巧!
*/
public class MaxSubStr {
public static String maxSubStr(String str1, String str2) {
if (str1 == null || str1.equals("") || str1 == null || str2.equals("")) {
return "";
}
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int len1 = c1.length;
int len2 = c2.length;
int[][] dp = new int[len1][len2];
// dp[i][j] 表示,最长公共子串必须以ij结尾时的长度
// 第一行
for (int i = 0; i < len2; i++) {
dp[0][i] = c2[i] == c1[0] ? 1 : 0;
}
// 第一列
for (int i = 1; i < len1; i++) {
dp[i][0] = c1[i] == c2[0] ? 1 : 0;
}
int max = 0;
int endIndex = 0;
for (int i = 1; i < len1; i++) {
for (int j = 1; j < len2; j++) {
if (c1[i] == c2[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
if(dp[i][j] > max){
max = dp[i][j];
endIndex = i;
}
}
}
return str1.substring(endIndex-max+1,endIndex+1);
}
public static String maxSubStr2(String str1,String str2){
if (str1 == null || str1.equals("") || str1 == null || str2.equals("")) {
return "";
}
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int len1 = c1.length;
int len2 = c2.length;
int row = 0;
int col = len2 - 1;
int len = 0;
int max = 0;
int endIndex = 0;
while (col >0 || row < len1){
int i = row;
int j = col;
while (i < len1 && j < len2){
if(c1[i] == c2[j]){
len ++;
}else {
len = 0;
}
if(len > max){
max = len;
endIndex = i;
}
i++;
j++;
}
if(col > 0){
col --;
}else {
row ++;
}
}
return str1.substring(endIndex-max+1,endIndex+1);
}
public static void main(String[] args) {
String str1 = "bnbdnvflop";
String str2 = "vblbdnvfmnk";
System.out.println(maxSubStr(str1, str2));
System.out.println(maxSubStr2(str1, str2));
}
}
- 给定一个由字符串组成的数组String[] strs,给定一个正数K,返回词频最大的前K个字符串,假设结果是唯一的
- 先用hash表统计每个字符串的词频
再用小根堆,围成前k个数的门槛,小根堆永远放的是当前遍历过的元素的前K大,堆顶是能进入小根堆的门槛。
/**
* 给定一个由字符串组成的数组String[] strs,给定一个正数K
* 返回词频最大的前K个字符串,假设结果是唯一的
*/
public class KOfWordFrequency {
public static List kOfWordFrequency(String[] strs,int k){
if(strs == null || strs.length == 0 || strs.length < k){
return null;
}
HashMap map = new HashMap<>();
for(String str : strs){
if(map.containsKey(str)){
map.put(str,map.get(str)+1);
}else {
map.put(str,1);
}
}
PriorityQueue> queue = new PriorityQueue<>(new MyComparator());
for (Map.Entry item :map.entrySet()){
if(queue.size() < k){
queue.offer(item);
}else {
if(queue.peek().getValue() < item.getValue()){
queue.poll();
queue.offer(item);
}
}
}
List res = new ArrayList<>();
while (!queue.isEmpty()){
res.add(queue.poll().getKey());
}
return res;
}
static class MyComparator implements Comparator>{
@Override
public int compare(Map.Entry o1, Map.Entry o2) {
return o1.getValue() - o2.getValue();
}
}
public static void main(String[] args) {
String[] strs = new String[]{"abc","abc","abc","bfg","ngh","bfg","mk","mk","mk","k","o","ol","ol"};
System.out.println(kOfWordFrequency(strs,3));
}
}
- 请实现如下结构:
TopRecord{
public TopRecord(int K) : 构造时事先指定好K的大小,构造后就固定不变了
public void add(String str) : 向该结构中加入一个字符串,可以重复加入
public Listtop() : 返回之前加入的所有字符串中,词频最大的K个
}
要求:
add方法,复杂度O(log K);
top方法,复杂度O(K)
// todo