力扣刷题之动态规划
241.为运算表达式设计优先级
分治+递归
class Solution {
public:
vector<int> diffWaysToCompute(string expression) {
// 存储中间值
vector<int> count;
for(int i = 0; i < expression.size(); i ++) {
char c = expression[i];
// 找到运算符
if(c == '+' || c == '-' || c == '*') {
// 运算符左边的运算结果
vector<int> left = diffWaysToCompute(expression.substr(0, i));
// 运算符右边的运算结果
vector<int> right = diffWaysToCompute(expression.substr(i + 1));
// 左右两边继续运算
for(int& l : left) {
for(int& r : right) {
switch(c) {
case '+':
count.push_back(l + r);
break;
case '-':
count.push_back(l - r);
break;
case '*':
count.push_back(l * r);
break;
}
}
}
}
}
// count为空说明当前无运算符,只是单独的数字,直接放入count中
if(count.size() == 0) {
count.push_back(stoi(expression));
}
return count;
}
};
263.丑数
class Solution {
public:
bool isUgly(int n) {
if (n <= 0) {
return false;
}
vector<int> factors = {2, 3, 5};
for (int factor : factors) {
while (n % factor == 0) {
n /= factor;
}
}
return n == 1;
}
};
264.丑数II
法一:最小堆+set
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> factors = {2, 3, 5};
unordered_set<long> seen;
priority_queue<long, vector<long>, greater<long>> heap;
seen.insert(1L);
heap.push(1L);
int ugly = 0;
for (int i = 0; i < n; i++) {
long curr = heap.top();
heap.pop();
ugly = (int)curr;
for (int factor : factors) {
long next = curr * factor;
if (!seen.count(next)) {
seen.insert(next);
heap.push(next);
}
}
}
return ugly;
}
};
313.超级丑数
class Solution {
public:
int nthSuperUglyNumber(int n, vector<int>& primes) {
vector<int> dp(n + 1); //用来存储丑数序列
dp[1] = 1; //第一个丑数是1
int m = primes.size();
vector<int> nums(m); //记录新丑数序列
vector<int> pointers(m, 1); //记录质数该与哪一位丑数做乘积
for (int i = 2; i <= n; i++) {
int minn = INT_MAX;
for (int j = 0; j < m; j++) {
nums[j] = dp[pointers[j]] * primes[j]; //旧丑数 * 质数序列 = 新丑数序列
minn = min(minn, nums[j]); //寻找所有新丑数中最小的丑数
}
dp[i] = minn;
for (int j = 0; j < m; j++)
if (minn == nums[j]) //如果此位置已经诞生过最小丑数
pointers[j]++; //让此位置所取旧丑数向后推一位
}
return dp[n];
}
};
338.比特位计数
法一:
class Solution {
public:
int countOnes(int x) {
int ones = 0;
while (x > 0) {
x &= (x - 1);
ones++;
}
return ones;
}
vector<int> countBits(int n) {
vector<int> bits(n + 1);
for (int i = 0; i <= n; i++) {
bits[i] = countOnes(i);
}
return bits;
}
};
法二:动态规划
class Solution {
public:
vector<int> countBits(int num) {
// 预先开好空间
vector<int> dp(num + 1);
// 同样不需要从0开始,因为dp[0] = 0
for(int i = 1; i <= num; i++){
if(i % 2 == 0){ // 偶数
dp[i] = dp[i / 2];
}
else{ // 奇数
dp[i] = dp[(i - 1) / 2] + 1;
}
}
// dp数组即为所求
return dp;
}
};
357.计算各个位数不同的数字个数
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
vector<int> dp(n+1);
for(int i = 2; i <= n; ++i)
dp[i] = dp[i-1]*10 + (9*pow(10, i-2) - dp[i-1])*(i-1);
int sum = 0;
for(auto& x : dp)
sum += x;
return pow(10, n) - sum;
}
};
368.最大整除子集(序列DP问题)(经典)
与300题 最长上升子序列思想类似
https://leetcode-cn.com/problems/largest-divisible-subset/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-0a3jc/
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<int> f(n, 0);
vector<int> g(n ,0);
for(int i = 0; i < n; i++) {
// 至少包含自身一个数,因此起始长度为 1,由自身转移而来
int len = 1, prev = i;
for(int j = 0; j < i; j++) {
if(nums[i] % nums[j] == 0) {
// 如果能接在更长的序列后面,则更新「最大长度」&「从何转移而来」
if(f[j] + 1 > len) {
len = f[j] + 1;
prev = j;
}
}
}
f[i] = len;
g[i] = prev;
}
// 遍历所有的 f[i],取得「最大长度」和「对应下标」
int idx = max_element(f.begin(), f.end()) - f.begin();
int max = f[idx];
// 使用 g[] 数组回溯出具体方案
vector<int> ans;
while(ans.size() != max) {
ans.push_back(nums[idx]);
idx = g[idx];
}
return ans;
}
};
375.猜数字大小II
https://leetcode-cn.com/problems/guess-number-higher-or-lower-ii/solution/dong-tai-gui-hua-c-you-tu-jie-by-zhang-xiao-tong-2/
class Solution {
public:
int getMoneyAmount(int n) {
if(n==1)
return 0;
//定义矩阵
int dp[n+1][n+1];
//初始化“\”
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
dp[i][j]=INT_MAX;
}
}
//定义基础值dp[i][i]
for(int i=0;i<=n;i++){
dp[i][i]=0;
}
//按列来,从第2列开始
for(int j=2;j<=n;j++){
//按行来,从下往上
for(int i=j-1;i>=1;i--){
//算除了两端的每一个分割点
for(int k=i+1;k<=j-1;k++){
dp[i][j]=min(k+max(dp[i][k-1],dp[k+1][j]),dp[i][j]);
}
//算两端
dp[i][j]=min(dp[i][j],i+dp[i+1][j]);
dp[i][j]=min(dp[i][j],j+dp[i][j-1]);
}
}
return dp[1][n];
}
};
300.最长上升子序列
法一:动态规划
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = (int)nums.size();
if (n == 0) {
return 0;
}
vector<int> dp(n, 0);
for (int i = 0; i < n; ++i) {
dp[i] = 1;
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
return *max_element(dp.begin(), dp.end());
}
};
法二:贪心+二分查找(labuladong耐心排序)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = 1, n = (int)nums.size();
if (n == 0) {
return 0;
}
vector<int> d(n + 1, 0);
d[len] = nums[0];
for (int i = 1; i < n; ++i) {
if (nums[i] > d[len]) {
d[++len] = nums[i];
} else {
int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
while (l <= r) {
int mid = (l + r) >> 1;
if (d[mid] < nums[i]) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
d[pos + 1] = nums[i];
}
}
return len;
}
};