1.金矿问题
有一位国王拥有5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人人数也不同。设黄金储量golden = {400, 500, 200, 300, 350},需要参与的工人数为person = {5, 5, 3, 4, 3}。
如果参与挖矿的工人的总数是10。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半的金矿。
要求用程序求出,最多能挖到多少黄金?
public static void main(String[] args) {
int w = 10;
int[] person = {5, 5, 3, 4, 3};
int[] golden = {400, 500, 200, 300, 350};
System.out.println("最多得到黄金: " + getBestGoldMining(w, person, golden));
}
private static int getBestGoldMining(int w, int[] person, int[] golden) {
int[] dp = new int[w + 1];
//依次判断每座金矿
for (int i = 0; i < golden.length; i++) {
//此处倒序,因为需要用到上一座金矿所产生的dp[j - person[i]]的值
for (int j = w; j > 0; j--) {
if (j >= person[i]) {
//判断此座金矿挖还是不挖,得到金矿更多
dp[j] = Math.max(dp[j], dp[j - person[i]] + golden[i]);
}
}
}
return dp[w];
}
2.硬币找零
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。
分析:dp(n) = min{dp(n), dp(n - conins[i]) + 1}
此题和金矿问题区别在于,每种硬币的数量是无限的,而金矿数组却是确定的。
public static void main(String[] args) {
int[] coins = {1, 2, 5};
int amount = 11;
System.out.println(coinChange(coins, amount));
}
private static int coinChange(int[] coins, int amount) {
if (amount == 0) {
return 0;
}
int[] dp = new int[amount + 1];
for (int i = 1; i <= amount; i++) {
//初始化dp[i],注意超出int取值范围
dp[i] = Integer.MAX_VALUE - 1;
for (int coin : coins) {
if (i >= coin) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] == Integer.MAX_VALUE - 1 ? -1 : dp[amount];
}
3.回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同起始索引或结束索引的子字符串即使由相同的字符组成,也会被计为不同的子字符串。
示例1:
输入: “abc”
输出: 3
说明:三个回文串:“a”,“b”,“c”。
示例2:
输入: “aaa”
输出: 6
说明:六个回文串:“a”,“a”,“a”,“aa”,“aa”,“aaa”。
分析:“回文串”是一个正读和反读都一样的字符串。
dp[i] [j] 表示字符串第 i 到 j 位的子串是否为回文串,当字符串第 i 位字符与第 j 位字符相同:
1> j - i = 0,同一个字符;
2> j - i = 1, ‘aa’,重复的两个字符;
3> j - i = 2, ‘aba’,两侧字符相同;
4> dp[i+1][j-1]也是回文串。
public static void main(String[] args) {
String str = "aaa";
System.out.println(childStrCount(str));
}
private static int childStrCount(String str) {
int length = str.length();
boolean[][] dp = new boolean[length][length];
int count = 0;
//因为判断dp[i][j]需要先判断dp[i + 1][j - 1], 所以i需要倒序,j需要正序遍历
for (int i = length - 1; i >= 0; i--) {
for (int j = i; j < length; j++) {
if (str.charAt(i) == str.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
count++;
}
}
}
return count;
}