Ones and Zeroes
In the computer world, use restricted resource you have to generate maximum benefit is what we always want to pursue.
For now, suppose you are a dominator of m 0s
and n 1s
respectively. On the other hand, there is an array with strings consisting of only 0s
and 1s
.
Now your task is to find the maximum number of strings that you can form with given m 0s
and n 1s
. Each 0
and 1
can be used at most once.
Note:
- The given numbers of
0s
and1s
will both not exceed100
- The size of given string array won't exceed
600
.
Example 1:
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 Output: 4 Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
可以看做背包问题的变种。对于每个子串而言都可以选或者不选。在第i个子串的状态可以用3个变量去描述:form的个数,剩余的0,剩余的1。不同的状态通过选还是不选进行转移。dp[j][k] = max(dp[j][k], dp[j-zeros][k-ones] + 1);
int findMaxForm(vector<string>& strs, int m, int n) {
int size = strs.size();
if(size == 0) return 0;
int dp[m+1][n+1];
for(int i = 0; i < m+1; i++){
fill_n(dp[i], n+1, 0);
}
for(int i = 0; i < strs.size(); i++){
int ones = 0, zeros = 0;
for(char c : strs[i]){
if(c == '1') ones++;
}
zeros = strs[i].size() - ones;
for(int j = m; j >= 0 && zeros <= j; j--){ //j和k是可以为0的,因为有的子串只含有1或0
for(int k = n; k >= 0 && ones <= k; k--){
dp[j][k] = max(dp[j][k], dp[j-zeros][k-ones] + 1);
}
}
}
return dp[m][n];
}
Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18]
,
The longest increasing subsequence is [2, 3, 7, 101]
, therefore the length is 4
解法1: O(N^2), DP dp[i] = max(dp[i], dp[j] + 1); -1 < j < i-1
int lengthOfLIS(vector<int>& nums) {
if(nums.empty()) return 0;
int dp[nums.size()];
fill_n(dp, nums.size(), 1);
int maxLen = 1;
for(int i = 1; i < nums.size(); i++){
for(int j = i-1; j > -1; j--){
if(nums[j] < nums[i]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
for(int len: dp) maxLen = max(maxLen, len);
return maxLen;
}
解法2:二分查找,O(NlogN)
1. 遍历nums并寻找subsequence的过程可以看作不断更新已知最长subsequence的过程。
2. 只有大于已知subsequence的最大数字才会添加到subsequence中
3. 更新总是将subsequence中的值替换为小于等于原数字的值,因此替换之后可能找到比原来更长的子序列
4. 一个非常详细的解释:http://www.geeksforgeeks.org/longest-monotonically-increasing-subsequence-size-n-log-n/
int lengthOfLIS(vector<int>& nums) {
vector<int> res;
for(int i=0; i<nums.size(); i++) {
auto it = std::lower_bound(res.begin(), res.end(), nums[i]);
if(it==res.end()) res.push_back(nums[i]);
else *it = nums[i];
}
return res.size();
}
Perfect Squares
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...
) which sum to n.
For example, given n = 12
, return 3
because 12 = 4 + 4 + 4
; given n = 13
, return 2
because 13 = 4 + 9
.
2. 需要注意的点是sqrt函数是非常耗时间的,尽量不要用
3. 此题还有数学解法:Legendre's three-square theorem
int numSquares(int n) {
int dp[n+1];
fill_n(dp, n+1, INT_MAX);
dp[0] = 0;
for(int i = 1; i <= n; i++){
int j = 1;
while(i - j*j >= 0) {
dp[i] = min(dp[i], dp[i - j*j] + 1);
++j;
}
}
return dp[n];
}
Guess Number Higher or Lower II
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.
However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.
Given a particular n ≥ 1 , find out how much money you need to have to guarantee a win .
1. 区间DP。dp[i][j]的定义:代表着如果我们在区间 [i , j] 内进行查找,所需要的最少 cost 来保证找到结果。
2. 如果以 top-down recursion 的方式分析这个问题,可以发现对于区间 [i, j] ,我们的猜测 i <= k <= j 我们可能出现以下三种结果:
1). k 就是答案,此时子问题的额外 cost = 0 ,当前位置总 cost = k + 0;
2). k 过大,此时我们的有效区间缩小为 [i , k - 1] 当前操作总 cost = k + dp[start][k - 1];
3). k 过小,此时我们的有效区间缩小为 [k + 1 , j] 当前操作总 cost = k + dp[k + 1][j];
由于我们需要 “保证得到结果”,也就是说对于指定 k 的选择,我们需要准备最坏情况 cost 是以下三种结果生成的 subproblem 中cost 最大的那个; 然而同时对于一个指定区间 [i , j] ,我们可以选择任意 i <= k <= j ,对于这个 k 的主观选择可以由我们自行决定,我们要选的是 k s.t. 其子问题的 cost + 当前操作 cost 最小的一个,至此,每次决策就构成了一次 MiniMax 的博弈。
3. 注意处理 k - 1 <= i 或者 j <= k + 1时,所需金额为0,因为只剩一个数了不用猜
int getMoneyAmount(int n) {
if(n == 1) return 0;
int dp[n+1][n+1];
for(int interval = 1; interval < n; interval++){ //从小到大遍历i和j的invertal,起始状态是所有的[i,i+1]
for(int i = 0; i + interval <= n; i++){
int j = i + interval;
dp[i][j] = INT_MAX;
for(int k = i; k <= j; k++){
dp[i][j] = min(dp[i][j], //选择总cost最小的k
k + max(k - 1 > i ? dp[i][k-1] : 0, j > k + 1 ? dp[k+1][j] : 0)); //选择子问题的最大cost
}
}
}
return dp[1][n];
}