class Solution {
public:
double ans1 = 0;
double ans2 = 0;
int dx[8] = {1, 1, -1, -1, 2, 2, -2, -2};
int dy[8] = {2, -2, 2, -2, 1, -1, 1, -1};
double mp[26][26][110];//从x,y,已经走了cnt步,还能出去的概率
double knightProbability(int n, int k, int row, int column) {
//找到所有可能
for(int i = 0; i < 26; i ++ )
for(int j = 0; j < 26; j ++ )
for(int k = 0; k < 110; k ++ )
{
mp[i][j][k] = -1;
}
dfs(n, k, 0, row, column);
return 1 - mp[row][column][0];
}
double dfs(int n, int k, int cnt, int x, int y)
{
if(x >= n || y >= n || x < 0 || y < 0)
{
return 1 / pow(8, cnt);//从上一步走了1步走出边界。
}
if(mp[x][y][cnt] != -1)//已经知道了这个位置要的概率和
{
return mp[x][y][cnt];
}
if(cnt == k)//走了k步未走出
{
mp[x][y][cnt] = 0;
return 0;
}
mp[x][y][cnt] = 0;//从0开始累加
for(int i = 0; i < 8; i ++ )
{
int nx = x + dx[i];
int ny = y + dy[i];
double t = dfs(n, k, cnt + 1, nx, ny);
mp[x][y][cnt] += t;
}
return mp[x][y][cnt];
}
};
动态规划解法
class Solution {
public:
vector<vector<int>> dirs = {{-2, -1}, {-2, 1}, {2, -1}, {2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}};
double knightProbability(int n, int k, int row, int column) {
vector<vector<vector<double>>> dp(k + 1, vector<vector<double>>(n, vector<double>(n)));
for (int step = 0; step <= k; step++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (step == 0) {
dp[step][i][j] = 1;
} else {
for (auto & dir : dirs) {
int ni = i + dir[0], nj = j + dir[1];
if (ni >= 0 && ni < n && nj >= 0 && nj < n) {
dp[step][i][j] += dp[step - 1][ni][nj] / 8;
}
}
}
}
}
}
return dp[k][row][column];
}
};
class Solution {
public:
int sum[20010];
int dp[4][20010];//到i的最大值,不是以i结尾
//有两种操作,1是不要i结尾的这个序列,那么就是从[j, i - 1]转移来的,2是要i这个序列,那么就是从[j - 1, i - k]转移过来
vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
int n = nums.size();
for(int i = 0; i < n; i ++ )
sum[i + 1] = sum[i] + nums[i];
vector<int> res;
int ans = 0;
for(int i = k; i <= n; i ++ )
{
for(int j = 1; j <= 3; j ++ )
dp[j][i] = max(dp[j][i - 1], dp[j - 1][i - k] + sum[i] - sum[i - k]);
ans = max(ans, dp[3][i]);
}
for(int i = 3; i >= 1; i --)
{
for(int j = k; j <= n; j ++ )
{
if(dp[i][j] == ans)
{
res.push_back(j - k);
ans -= sum[j] - sum[j - k];
break;
}
}
}
reverse(res.begin(), res.end());
return res;
}
};
记忆化搜索,超级快
class Solution {
public:
int book[210][210];
int getMoneyAmount(int n) {
return dfs(1, n);
}
int dfs(int l, int r)
{
if(book[l][r] != 0) return book[l][r];
if(l >= r) return 0;
int res = 1e9;
for(int i = (l + r) >> 1; i < r; i ++ )
{
res = min(res, max(dfs(l, i - 1), dfs(i + 1, r)) + i);
}
book[l][r] = res;
return res;
}
};
区间dp,动态规划,可以发现枚举中间点时不会低于(i + j) >> 1
, 也不会枚举最后j
class Solution {
public:
int dp[210][210];
int getMoneyAmount(int n) {
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i <= n; i ++ ) dp[i][i] = 0, dp[i][i - 1] = 0;
for(int l = 2; l <= n; l ++ )
for(int i = 1; i + l - 1 <= n; i ++ )
{
int j = i + l - 1;
for(int k = (i + j) >> 1; k < j; k ++ )
{
dp[i][j] = min(dp[i][j], max(dp[i][k - 1], dp[k + 1][j]) + k);
}
}
return dp[1][n];
}
};
记忆化搜索
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int t) {
return dfs(nums, t, 0, 0);
}
unordered_map<string, int> mp;
int dfs(vector<int> & nums, int t, int u, int cur)
{
string key = to_string(u) + '-' + to_string(cur);//用这个标记结果
if(mp.count(key)) return mp[key];
if(u == nums.size()){
if(t == cur)
mp[key] = 1;
else mp[key] = 0;
return mp[key];
}
int left = dfs(nums, t, u + 1, cur + nums[u]);
int right = dfs(nums, t, u + 1, cur - nums[u]);
mp[key] = left + right;
return mp[key];
}
};
动态规划
class Solution {
public:
int dp[1010];
int findTargetSumWays(vector<int>& nums, int target) {
//可以先全部加起来,然后01背包,然后是恰好是这个容量的方案数,因为是减,所以用0开始
int sum = 0;
for(auto x: nums) sum += x;
dp[sum] = 1;//意思是全都不要的方案数为1
for(int i = 0; i < nums.size(); i ++ )
for(int j = abs(target); j + 2 * nums[i] <= sum; j ++ )
dp[j] += dp[j + 2* nums[i]];//意思要了i则从dp[j + nums[i]]转移到dp[j]
return dp[abs(target)];
}
};
class Solution {
const int p = 1e9 + 7;
public:
int F[110];
int fib(int n) {
if(F[n] != 0) return F[n];
if(n == 0) return 0;
if(n == 1) return 1;
F[n] = (fib(n - 1) + fib(n - 2)) % p;
return F[n];
}
};
记忆化搜索
class Solution {
public:
unordered_map<int, bool> f;
unordered_map<string, bool> mp;
//f表示有没有这个石子,mp表示行不行得通
bool canCross(vector<int>& stones) {
//可以用上一步的k以及到达的步数标记点
for(int i = 0; i < stones.size(); i ++ )
f[stones[i]] = true;//所有步数表示的下标
return dfs(stones, 0, 0);
}
int dfs(vector<int> & stones, int k, int cur)
{
string key = to_string(k) + "-" + to_string(cur);
if(mp.count(key)) return mp[key];
if(cur == stones[stones.size() - 1])//走到最后了
{
mp[key] = true;
return true;
}
int kk[3] = {k - 1, k , k + 1};
bool p = false;
for(int i = 0; i < 3; i ++ )
{
if(kk[i] >= 1 && f[cur + kk[i]]) //有这个石子
{
p |= dfs(stones, kk[i], cur + kk[i]);
if(p) break;
}
}
mp[key] = p;
return p;
}
};
动态规划
class Solution {
public:
// state: dp[i][j] -》现在在i,到i用了j步,能否到
// dp[n][i] for i in range(0, n) if dp[n][i]
bool canCross(vector<int>& stones) {
int n = stones.size();
vector<vector<bool>> dp(n, vector<bool>(n, false));
dp[0][0] = true;
for (int i = 1; i < n; ++i) {
for (int j = i - 1; j >= 0; --j) {
int k = stones[i] - stones[j];
if (k > j + 1) break;
dp[i][k] = dp[j][k] || dp[j][k - 1] || dp[j][k + 1];
}
}
for (auto i : dp[n - 1]) if (i == true) return true;
return false;
}
};
记忆化搜索
class Solution {
public:
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int mp[55][55][55];
const int p = 1e9 + 7;
int findPaths(int m, int n, int bu, int x, int y) {
memset(mp, 0xcf, sizeof(mp));
return findPath(m, n, bu, x, y);
}
int findPath(int m, int n, int bu, int x, int y) {
if(y < 0 || x < 0 || x >=m || y >= n)
return 1;
if(!bu) return 0;
if(mp[x][y][bu] != 0xcfcfcfcf) return mp[x][y][bu];
int ans = 0;
for(int i = 0; i < 4; i ++ )
{
ans += findPath(m, n, bu - 1, x + dx[i], y + dy[i]) % p;
ans %= p;
}
mp[x][y][bu] = ans;
return ans % p;
}
};
记忆化搜索
class Solution {
const int p = 1e9 + 7;
public:
int mp[100010][2][3];
int checkRecord(int n) {
//状态表示[][][]
memset(mp, 0xcf, sizeof(mp));
return dfs(n, 0, 0, 0);
}
int dfs(int n, int cur, int A_cnt, int L_state)
{
if(mp[cur][A_cnt][L_state] != 0xcfcfcfcf) return mp[cur][A_cnt][L_state];
if(cur == n)
return 1;
int res = 0;
if(A_cnt < 1) //缺勤次数少于1
res += dfs(n, cur + 1, A_cnt + 1, 0);
res %= p;
if(L_state < 2)//前边没有连续两天迟到
res += dfs(n, cur + 1, A_cnt, L_state + 1);//保留两天
res %= p;
res += dfs(n, cur + 1, A_cnt, 0);
mp[cur][A_cnt][L_state] = res % p;
return mp[cur][A_cnt][L_state];
}
};
动态规划
class Solution {
public:
static constexpr int MOD = 1'000'000'007;
int checkRecord(int n) {
int dp[2][3]; // A 的数量,结尾连续 L 的数量
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
int dpNew[2][3]; // A 的数量,结尾连续 L 的数量
memset(dpNew, 0, sizeof(dpNew));
// 以 P 结尾的数量
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
dpNew[j][0] = (dpNew[j][0] + dp[j][k]) % MOD;
}
}
// 以 A 结尾的数量
for (int k = 0; k <= 2; k++) {
dpNew[1][0] = (dpNew[1][0] + dp[0][k]) % MOD;
}
// 以 L 结尾的数量
for (int j = 0; j <= 1; j++) {
for (int k = 1; k <= 2; k++) {
dpNew[j][k] = (dpNew[j][k] + dp[j][k - 1]) % MOD;
}
}
memcpy(dp, dpNew, sizeof(dp));
}
int sum = 0;
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
sum = (sum + dp[j][k]) % MOD;
}
}
return sum;
}
};
class Solution {
public:
bool isScramble(string &s1, string &s2) {
//记忆化搜索
return dfs(s1, s2);
}
unordered_map<string, bool> mp;
bool dfs(string s1, string s2)
{
string key = s1 + '-' + s2;
if(mp.count(key)) return mp[key];
if(s1 == s2)
{
mp[key] = true;
return true;
}
if(!check(s1, s2))
{
mp[key] = false;
return false;
}
bool flag = false;
int l = 0, r = s1.size() - 1;
//l -- i i + 1 --- r
for(int i = 0; i < r; i ++ ){
string x = s1.substr(l, i - l + 1);
string y = s1.substr(i + 1, r - i);
flag |= dfs(x, s2.substr(l, i - l + 1)) && dfs(y, s2.substr(i + 1, r - i));
flag |= dfs(x, s2.substr(r - i + l, i - l + 1)) && dfs(y, s2.substr(l, r - i));
}
mp[key] = flag;
return flag;
}
bool check(string &s1, string &s2) {
if(s1.size() != s2.size()) return false;
vector<int> cnt1(26,0), cnt2(26,0);
for(auto c : s1)
cnt1[c-'a']++;
for(auto c : s2)
cnt2[c-'a']++;
return cnt1 == cnt2;
}
};
class Solution {
public:
int findSubstringInWraproundString(string p) {
vector<int> dp(26);
int k = 0;
for (int i = 0; i < p.length(); ++i) {
if (i && (p[i] - p[i - 1] + 26) % 26 == 1) { // 字符之差为 1 或 -25
++k;
} else {
k = 1;
}
dp[p[i] - 'a'] = max(dp[p[i] - 'a'], k);
}
return accumulate(dp.begin(), dp.end(), 0);
}
};
记忆化搜索
class Solution {
public:
string s, p;
int m, n;
unordered_map<int, unordered_map<int, bool>> mp;
bool isMatch(string _s, string _p) {
s = _s, p = _p;
m = s.size(), n = p.size();
return dfs(0 ,0);
}
bool dfs(int i, int j) //s 到 i , p 到 j
{
if(j == n) return i == m;//p用完了可以结束
if(mp[i].count(j)) return mp[i][j];
bool res = false;
if(j + 1 < n && p[j + 1] == '*')
{
if(i < m && (s[i] == p[j] || p[j] == '.'))
res = dfs(i, j + 2) || dfs(i + 1, j + 2) || dfs(i + 1, j);//?* 匹配0个、匹配1个、匹配多个
else
res = dfs(i, j + 2);
}
else if(i < m && (s[i] == p[j] || p[j] == '.'))
res = dfs(i + 1, j + 1);
mp[i][j] = res;
return res;
}
};
动态规划
class Solution {
public:
bool match(int i, int j, string p, string s)
{
if(i == 0) return false;
if(p[j - 1] == '.')
return true;
return s[i - 1] == p[j - 1];
}
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
vector<vector<int>> f(m + 1, vector<int>(n + 1));
f[0][0] = true;
for(int i = 0; i <= m; i ++ )
for(int j = 1; j <= n; j ++ )
{
if(p[j - 1] == '*')
{
f[i][j] |= f[i][j - 2];
if(match(i, j - 1, p, s))
f[i][j] |= f[i - 1][j];
}
else
{
if(match(i, j, p, s))
f[i][j] |= f[i - 1][j - 1];
}
}
return f[m][n];
}
};
class Solution {
public:
string s, p;
int m, n;
unordered_map<int, unordered_map<int, bool>> mp;
bool isMatch(string _s, string _p) {
if(_s == "")
{
_s = 'a';
_p = _p.append("a");
}
s = _s, p = _p;
m = s.size(), n = p.size();
return dfs(0 ,0);
}
bool dfs(int i, int j) //s 到 i , p 到 j
{
if(j == n) return i == m;//p用完了可以结束
if(mp[i].count(j)) return mp[i][j];
bool res = false;
if(i < m && p[j] == '*')
{
res = dfs(i, j + 1) || dfs(i + 1, j) || dfs(i + 1, j + 1);
}
else if(i < m && (s[i] == p[j] || p[j] == '?'))
res = dfs(i + 1, j + 1);
if(i == m && p[j] == '*') res |= dfs(i, j + 1);
mp[i][j] = res;
return res;
}
};
class Solution {
public:
int numDecodings(string s) {
vector<int> dp(s.size() + 1);
dp[0] = 1;
for(int i = 1; i <= s.size(); i ++ )
{
if(s[i - 1] != '0')
dp[i] += dp[i - 1];
if(i > 1 && s[i - 2] != '0' && (s[i - 2] - '0') * 10 + s[i - 1] - '0' <= 26)
dp[i] += dp[i - 2];
}
return dp[s.size()];
}
};
class Solution {
public:
int numDistinct(string s, string t) {
//dp[i][j] s到i,t到j的子序列个数
int m = s.size(), n = t.size();
vector<unsigned> dp(n + 1);
dp[0] = 1;
for(int i = 1; i <= m; i ++ )
for(int j = n; j >= 1; j -- )
if(s[i - 1] == t[j - 1])
dp[j] += dp[j - 1];
return dp[n];
}
};
ONE = {'1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1, '8': 1, '9': 1, '*': 9}
TWO = {'10': 1, '11': 1, '12': 1, '13': 1, '14': 1, '15': 1, '16': 1, '17': 1, '18': 1, '19': 1, '20': 1,
'21': 1, '22': 1, '23': 1, '24': 1, '25': 1, '26': 1, '*0': 2, '*1': 2, '*2': 2, '*3': 2, '*4': 2,
'*5': 2, '*6': 2, '*7': 1, '*8': 1, '*9': 1, '1*': 9, '2*': 6, '**': 15}
class Solution:
def numDecodings(self, s: str) -> int:
dp = 1, ONE.get(s[:1], 0)
for i in range(1, len(s)):
dp = dp[1], (ONE.get(s[i], 0) * dp[1] + TWO.get(s[i - 1: i + 1], 0) * dp[0]) % 1000000007
return dp[-1]
class Solution {
public:
int minSteps(int n) {
//打印出n个A的最少操作
vector<int> f(n + 1, n + 1);
f[1] = 0;
for(int i = 2; i <= n; i ++ )
for(int j = 1; j * j <= i; j ++)
{
if(i % j == 0) //从j复制过来
{
f[i] = min(f[i], f[j] + i / j);
f[i] = min(f[i], f[i / j] + j);
}
}
return f[n];
}
};
记忆化搜索
class Solution {
public:
vector<int> be;
vector<vector<int>> mp;
int maxValue(vector<vector<int>>& events, int k) {
mp = vector<vector<int>>(events.size() + 1, vector<int>(k, INT_MIN));
sort(events.begin(), events.end());
for(auto t : events)
be.push_back(t[0]);
return dfs(0, 0, events, k);
}
int dfs(int cur, int sum, vector<vector<int>>& events, int k)
{
if(sum >= k) return 0;//sum 超了返回
if(mp[cur][sum] != INT_MIN) return mp[cur][sum];
if(cur == events.size() - 1) //如果能参加最后一个就返回
return events[cur][2];
//如果不参加这一个
int res = 0;
res = max(res, dfs(cur + 1, sum, events, k));//看看下一个能参加吗
//如果参加这个
int res2 = events[cur][2];
auto it = upper_bound(be.begin() + cur, be.end(), events[cur][1]);//找到继续参加的会议
if(it != be.end())
res2 += dfs(it - be.begin(), sum + 1, events, k);
res = max(res, res2);
mp[cur][sum] = res;
return res;
}
};
动态规划(差不多快)
class Solution {
public:
static bool cmp(const vector<int>& x, const vector<int>& y){
return x[1] < y[1];//定义vector排序函数
}
int maxValue(vector<vector<int>>& events, int k) {
int n = events.size();
sort(events.begin(), events.end(), cmp);
vector<vector<int>> dp(n, vector<int>(k + 1, INT_MIN));
dp[0][0] = 0;
dp[0][1] = events[0][2];
for(int i = 1; i < n; i ++ ){
//如果参加会议,二分查找前一个可以参加的会议也就是结束时间小的
int l = 0, r = i - 1;
int next = -1;
while(l <= r)
{
int mid = (l + r) / 2;
if(events[mid][1] < events[i][0]){
next = max(next, mid);
l = mid + 1;
}else{
r = mid - 1;
}
}
if(next != -1){
for(int j = 1; j <= k; j ++ ){
dp[i][j] = max(dp[i][j], dp[next][j - 1] + events[i][2]);
}
}else{
//没有下一个了,当前只会参加1次会议
dp[i][1] = max(dp[i][1], events[i][2]);
}
//不参加会议
for(int j = 0; j <= k; j ++ ){
dp[i][j] = max(dp[i][j], dp[i - 1][j]);
}
}
int ans = 0;
for(int i = 1; i <= k; i ++ ){
ans = max(ans, dp[n - 1][i]);
}
return ans;
}
};
【宫水三叶】如何抽象成二维问题进行求解 - 使所有区间的异或结果为零 - 力扣(LeetCode)
class Solution {
private:
// x 的范围为 [0, 2^10)
static constexpr int MAXX = 1 << 10;
// 极大值,为了防止整数溢出选择 INT_MAX / 2
static constexpr int INFTY = INT_MAX / 2;
public:
int minChanges(vector<int>& nums, int k) {
int n = nums.size();
vector<int> f(MAXX, INFTY);
// 边界条件 f(-1,0)=0
f[0] = 0;
for (int i = 0; i < k; ++i) {
// 第 i 个组的哈希映射
unordered_map<int, int> cnt;
int size = 0;
for (int j = i; j < n; j += k) {
++cnt[nums[j]];
++size;
}
// 求出 t2
int t2min = *min_element(f.begin(), f.end());
vector<int> g(MAXX, t2min);
for (int mask = 0; mask < MAXX; ++mask) {
// t1 则需要枚举 x 才能求出
for (auto [x, countx]: cnt) {
g[mask] = min(g[mask], f[mask ^ x] - countx);
}
}
// 别忘了加上 size
for(auto &x : g)
x += size;
f = g;
}
return f[0];
}
};
class Solution {
public:
int dp[20][20];
int numWays(int n, vector<vector<int>>& relation, int k) {
//dp[][] 到达i经过j轮
dp[0][0] = 1;
for(int i = 1; i <= k; i ++ )
{
for(auto t: relation)
{
int a = t[0], b = t[1];
dp[b][i] += dp[a][i - 1];
}
}
return dp[n - 1][k];
}
};
十分注意dp的初始状态
class Solution {
public:
int dp[510][510];
int minDistance(string word1, string word2) {
int n = word1.size(), m = word2.size();
memset(dp, 0x3f, sizeof dp);
dp[0][0] = 0;
for(int i = 1; i <= n; i ++ ) dp[i][0] = i;
for(int j = 1; j <= m; j ++ ) dp[0][j] = j;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
{
//插入一个字符
dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1);
//删除一个字符
dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
//替换一个字符
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + (word1[i - 1] != word2[j - 1]));
}
return dp[n][m];
}
};
class Solution {
public:
int maxEnvelopes(vector<vector<int>>& envelopes) {
//对一个进行排序,找到最长上升子序列
sort(envelopes.begin(), envelopes.end(), [](const auto& e1, const auto& e2) {
return e1[0] < e2[0] || (e1[0] == e2[0] && e1[1] > e2[1]);//保证如果宽度相等的话,只要第一个,后边的不要
});
int n = envelopes.size();
//dp[]以i结尾的最长上升子序列的长度
vector<int> f = {envelopes[0][1]};
for (int i = 1; i < n; ++i) {
if (int num = envelopes[i][1]; num > f.back()) {//如果大可以放后边
f.push_back(num);
}
else {
auto it = lower_bound(f.begin(), f.end(), num);//如果小,找到第一个大于等于它的值修改,
*it = num;
}
}
return f.size();
}
};
//可以分情况看,如果这个数最后用上了,那么一定是一些小的数+num, 插入的很合理
//如果没有用上,也就是后边的数可能很小,那么序列的最长长度也不会改变。
树状数组
class Solution {
public:
int g[10010];
static bool cmp(vector<int> &a, vector<int> &b)
{
if(a[0] == b[0]) return a[1] > b[1];
return a[0] < b[0];
}
int lowbit(int x){
return x & -x;
}
void update(int x, int val)
{
for(int i = x; i <= 10000; i += lowbit(i))
{
g[i] = max(g[i], val);
}
}
int find(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i))
{
res = max(res, g[i]);
}
return res;
}
int maxEnvelopes(vector<vector<int>>& envelopes) {
//排序后,如果大于的话,找到所有小于的值的最大值
sort(envelopes.begin(), envelopes.end(), cmp);
int n = envelopes.size();
int ans = 0;
int f[n];
for(int i = 0; i < n; i ++ )
{
int mlast = find(envelopes[i][1] - 1);
f[i] = mlast + 1;
update(envelopes[i][1], f[i]);
ans = max(ans, f[i]);
}
return ans;
}
};
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<int> dp(n);
int num = -1;
//以i结尾的最长长度
int p, ma = 0;
for(int i = 0; i < n; i ++ )
{
dp[i] = 1;
for(int j = 0; j < i; j ++ )
{
if(nums[i] % nums[j] == 0)
dp[i] = max(dp[j] + 1, dp[i]);
}
if(ma < dp[i])
{
ma = dp[i];
p = i;
}
}
vector<int> ans;
while(ma >= 1)
{
if(num == -1)
{
ans.push_back(nums[p]);
ma --;
num = nums[p];
}
else
{
while(dp[p] != ma || num % nums[p] != 0) p --;//找到对对应结果
ans.push_back(nums[p]);
ma --;
num = nums[p];
}
}
return ans;
}
};
引入一个弱等差序列的概念,即2及以上长度的序列,当两个序列合并时,可以计算数组的数量
class Solution {
public:
int numberOfArithmeticSlices(vector<int> &nums) {
int ans = 0;
int n = nums.size();
vector<unordered_map<long long, int>> f(n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
long long d = 1LL * nums[i] - nums[j];
auto it = f[j].find(d);
int cnt = it == f[j].end() ? 0 : it->second;
ans += cnt;
f[i][d] += cnt + 1;
}
}
return ans;
}
};
class Solution {
public:
int numberOfArithmeticSlices(vector<int> &nums) {
int ans = 0;
int n = nums.size();
vector<int> f(n);
for (int i = 0; i < n; ++i) {
f[i] = 1;
if(i < 2) continue;
int cnt = nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2] ? f[i - 1] : 0;
ans += cnt;
f[i] += cnt;
}
return ans;
}
};
class Solution {
public:
int minDistance(string word1, string word2) {
//空字符串也行
//使[i, j] 成立的最小步数
int n = word1.size(), m = word2.size();
vector<vector<int>> dp(n + 1, vector<int> (m + 1));
for(int i = 1; i <= n; i ++ ) dp[i][0] = i;
for(int j = 1; j <= m; j ++ ) dp[0][j] = j;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
{
if(word1[i - 1] == word2[j - 1])//可以不删
{
dp[i][j] = min({dp[i - 1][j - 1], dp[i][j - 1] + 1, dp[i - 1][j] + 1});
}
else//必须删
{
dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i][j - 1] + 1, dp[i - 1][j] + 1});
}
}
return dp[n][m];
}
};
class Solution {
public:
static const int MOD = 1e9 + 7;
int kInversePairs(int n, int k) {
//恰好拥有k个逆序对
//dp[][] i个数,恰好有 j 个逆序对
vector<vector<int>> dp(n + 1, vector<int>(k + 1));
dp[0][0] = 1;
for(int i = 1; i <= n; i ++ )
{
//第i个数有 i 种插入方式,分别会使 逆序 增加 0 -- i - 1
int sum = 0;
int cnt = 0;
for(int j = 0; j <= k; j ++ )
{
if(j <= i - 1)
{
sum += dp[i - 1][j];
}
else
{
sum += dp[i - 1][j];
sum -= dp[i - 1][cnt];
sum = (sum % MOD + MOD ) % MOD;
cnt ++;
}
dp[i][j] += sum;
sum %= MOD;
dp[i][j] %= MOD;
}
// for(int p = 0; p <= min(j, i - 1); p ++ ) 0 --- 0 0 ---- 1
// { 0 --- 0 0 ---- j 1 --- j
// dp[i][j] += dp[i - 1][j - p];
// dp[i][j] %= MOD;
// }
}
return dp[n][k];
}
};
还可以树状数组优化
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
//最长递增子序列的个数是不递增的长度
int n = nums.size();
vector<int> dp(n);//以i结尾的长度
vector<int> cnt(n);//以i结尾的数量
for(int i = 0; i < n; i ++ )
{
dp[i] = 1;
cnt[i] = 1;
for(int j = 0; j < i; j ++ )
{
if(nums[i] > nums[j])
{
if(dp[i] == dp[j] + 1)//可以累加
{
cnt[i] += cnt[j];
}
else if(dp[i] < dp[j] + 1)
{
dp[i] = dp[j] + 1;
cnt[i] = cnt[j];
}
}
}
}
int res = *max_element(dp.begin(), dp.end());
int ans = 0;
for(int i = 0; i < cnt.size(); i ++ )
{
if(res == dp[i])
ans += cnt[i];
}
return ans;
}
};
class Solution {
public:
int mp[10010];
int dp[10010][2];//要还是不要这个
int deleteAndEarn(vector<int>& nums) {
for(auto x: nums)
mp[x] ++;
for(int i = 1; i <= 10000; i ++ )
{
dp[i][0] = dp[i - 1][1] + mp[i] * i;//要这个一定不要上一个
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0]);//不要这个
}
return max(dp[10000][0], dp[10000][1]);
}
};
class Solution {
public:
typedef long long LL;
int maxTurbulenceSize(vector<int>& arr) {
//比较符号翻转
int n = arr.size();
vector<int> dp(n);
dp[0] = 1;
for(int i = 1; i < n; i ++ )
{
dp[i] = 1;
if(arr[i] == arr[i - 1]) continue;
if(dp[i - 1] <= 1) dp[i] = dp[i - 1] + 1;
else
{
if((LL)(arr[i] - arr[i - 1]) * (arr[i - 2] - arr[i - 1]) > 0)
dp[i] = dp[i - 1] + 1;
else
dp[i] = 2;
}
}
return *max_element(dp.begin(), dp.end());
}
};
class Solution {
public:
unordered_map<int, int> mp;
int longestSubsequence(vector<int>& arr, int difference) {
//超时
int n = arr.size();
vector<int> dp(n);
for(int i = 0; i < n; i ++ )
{
dp[i] = 1;
dp[i] = max(dp[i], mp[arr[i]] + 1);
mp[arr[i] + difference] = max(mp[arr[i] + difference], dp[i]);
}
return *max_element(dp.begin(), dp.end());
}
};
官方题解
class Solution {
public:
int longestSubsequence(vector<int> &arr, int difference) {
int ans = 0;
unordered_map<int, int> dp;
for (int v: arr) {
dp[v] = dp[v - difference] + 1;
ans = max(ans, dp[v]);
}
return ans;
}
};
class Solution {
public:
int dp[110][22][110];
int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
//dp[][][] 到i且i涂成j颜色组成 k个街区的最小花费
memset(dp, 0x3f, sizeof(dp));
if(houses[0] == 0)
{
for(int j = 0; j < n; j ++ )
{
dp[0][j][1] = cost[0][j];//涂某种颜色,产生一个街区。
}
}
else dp[0][houses[0] - 1][1] = 0;//已经涂过了
for(int i = 1; i < m; i ++ )//第几个房子
{
if(houses[i] == 0)
{
for(int j = 0; j < n; j ++ )//第几个颜色
{
for(int k = 1; k <= i + 1; k ++ )//组成多少街区
{
for(int p = 0; p < n; p ++ )//上一个是什么颜色
{
if(p == j)//街区不变
{
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k] + cost[i][j]);
}
else//街区增加
{
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k - 1] + cost[i][j]);
}
}
}
}
}
else
{
int j = houses[i] - 1;//不用涂色
for(int k = 1; k <= i + 1; k ++ )//组成多少街区
{
for(int p = 0; p < n; p ++ )//上一个是什么颜色
{
if(p == j)
{
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k]);
}
else
{
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k - 1]);
}
}
}
}
}
int ans = 1e9;
for(int j = 0; j < n; j ++ )
{
ans = min(ans, dp[m - 1][j][target]);//最后是哪一种种颜色
}
if(ans == 1e9) return -1;
return ans;
}
};
class Solution {
public:
int dp[1010][1010];
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size(), n = text2.size();
for(int i = 1; i <= m; i ++ )
{
for(int j = 1; j <= n; j ++ )
{
if(text1[i - 1] == text2[j - 1])//相等就累加
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else //不相等i 和 j 转移过来
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
};
class Solution {
public:
int minOperations(vector<int>& target, vector<int>& arr) {
int n = target.size();
unordered_map<int, int> pos;
for(int i = 0; i < n; i ++ ){
pos[target[i]] = i;
}
vector<int> d;
for(int val: arr)
{
if(pos.count(val)){
int idx = pos[val];
auto it = lower_bound(d.begin(), d.end(), idx);
if(it != d.end())
*it = idx;
else
d.push_back(idx);
}
}
return n - d.size();
}
};
在这里插入代码片class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
//组合个数
//恰好为target的方案总数
int n = nums.size();
int dp[target + 1];
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for(int j = 1; j <= target; j ++ )
for(int i = 1; i <= n; i ++ )
{
if(j >= nums[i - 1] && dp[j - nums[i - 1]] < INT_MAX - dp[j])
{
dp[j] += dp[j - nums[i - 1]];
}
}
return dp[target];
}
};
class Solution {
public:
//恰好target
//成本cost
//价值为 i + 1,而且不断增加
//一个思路是先找到能够实现的最高位数, 然后找到这个位数的最佳方案
int f[5500];
string largestNumber(vector<int>& cost, int target) {
memset(f, 0xcf, sizeof(f));
f[0] = 0;
for(int i = 1; i <= 9; i ++ )
{
for(int j = cost[i - 1]; j <= target; j ++)
{
f[j] = max(f[j], f[j - cost[i - 1]] + 1);//每个的价值都是1
}
}
string ans = "";
int w = target;
for(int i = 1; i <= f[target]; i ++ )//每位都选一个数的最大值
{
for(int k = 9; k >= 1; k -- )//从大向小选
{
if(w - cost[k - 1] >= 0 && f[w] == f[w - cost[k - 1]] + 1)//选完这个数之后最多位数减1,则这一位选择个
{
w -= cost[k - 1];
ans += (char)(k + '0');
break;
}
}
}
if(ans == "") return "0";
return ans;
}
};
class Solution {
public:
map<vector<int>, int> mp;
//记忆化搜索 满足需求的最小价格,使用map存储
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
int n = price.size();
return dfs(price, needs, special, n);
}
// 记忆化搜索计算满足购物清单所需花费的最低价格
int dfs(vector<int> price, vector<int> curNeeds, vector<vector<int>> & special, int n) {
if (!mp.count(curNeeds)) {
int minPrice = 0;
for (int i = 0; i < n; ++i) {
minPrice += curNeeds[i] * price[i]; // 不购买任何大礼包,原价购买购物清单中的所有物品
}
for (auto & x : special) {
int specialPrice = x[n];
vector<int> nxtNeeds;
for (int i = 0; i < n; ++i) {
if (x[i] > curNeeds[i]) { // 不能购买超出购物清单指定数量的物品
break;
}
nxtNeeds.emplace_back(curNeeds[i] - x[i]);
}
if (nxtNeeds.size() == n) { // 大礼包可以购买
minPrice = min(minPrice, dfs(price, nxtNeeds, special, n) + specialPrice);
}
}
mp[curNeeds] = minPrice;
}
return mp[curNeeds];
}
};
class Solution {
public:
//n名员工,选择一些工作,求出满足最小利润的方案
//价值和容量可以互换
//f[N][N] 价值N, 体积N的方案数
int f[110][110];
const int p = 1e9 + 7;
int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
int sum = 0;
for(auto x: profit) sum += x;
for(int i = 0; i <= n; i ++ )
f[0][i] = 1;
for(int i = 0; i < group.size(); i ++ )
for(int j = minProfit; j >= 0; j -- )
for(int k = n; k >= group[i]; k -- )
{
f[j][k] += f[max(0, j - profit[i])][k - group[i]];
f[j][k] %= p;
}
return f[minProfit][n];
}
};
【宫水三叶】一题三解]
class Solution {
public:
bool isScramble(string &s1, string &s2) {
//记忆化搜索
return dfs(s1, s2);
}
unordered_map<string, bool> mp;
bool dfs(string s1, string s2)
{
string key = s1 + '-' + s2;
if(mp.count(key)) return mp[key];
if(s1 == s2)
{
mp[key] = true;
return true;
}
if(!check(s1, s2))
{
mp[key] = false;
return false;
}
bool flag = false;
int l = 0, r = s1.size() - 1;
//l -- i i + 1 --- r
for(int i = 0; i < r; i ++ ){
string x = s1.substr(l, i - l + 1);
string y = s1.substr(i + 1, r - i);
flag |= dfs(x, s2.substr(l, i - l + 1)) && dfs(y, s2.substr(i + 1, r - i));
flag |= dfs(x, s2.substr(r - i + l, i - l + 1)) && dfs(y, s2.substr(l, r - i));
}
mp[key] = flag;
return flag;
}
bool check(string &s1, string &s2) {
if(s1.size() != s2.size()) return false;
vector<int> cnt1(26,0), cnt2(26,0);
for(auto c : s1)
cnt1[c-'a']++;
for(auto c : s2)
cnt2[c-'a']++;
return cnt1 == cnt2;
}
};
class Solution {
public:
int dp[210][210];
int getMoneyAmount(int n) {
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i <= n; i ++ ) dp[i][i] = 0, dp[i][i - 1] = 0;
for(int l = 2; l <= n; l ++ )
for(int i = 1; i + l - 1 <= n; i ++ )
{
int j = i + l - 1;
for(int k = (i + j) >> 1; k < j; k ++ )
{
dp[i][j] = min(dp[i][j], max(dp[i][k - 1], dp[k + 1][j]) + k);
}
}
return dp[1][n];
}
};
一开始的解
class Solution {
public:
int dp[1010][1010];
int longestPalindromeSubseq(string s) {
//动态规划
//区间i --- j 回文串的长度
int n = s.size();
for(int i = 0; i < n; i ++ )
dp[i][i] = 1;
for(int len = 2; len <= n; len ++ )
{
for(int i = 0; i + len - 1 < n; i ++ )
{
int j = i + len - 1;
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
for(int k = i; k < j; k ++ )
{
if(s[j] == s[k])
{
dp[i][j] = max(dp[i][j], dp[k + 1][j - 1] + 2);
break;//找到第一个符合要求的即可
}
}
}
}
return dp[0][n - 1];
}
};
正解
class Solution {
public:
int dp[1010][1010];
int longestPalindromeSubseq(string s) {
//动态规划
//区间i --- j 回文串的长度
int n = s.size();
for(int i = 0; i < n; i ++ )
dp[i][i] = 1;
for(int len = 2; len <= n; len ++ )
{
for(int i = n - len; i >= 0; i --)
{
int j = i + len - 1;
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
if(s[i] == s[j])
{
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
return dp[0][n - 1];
}
};
class Solution {
public:
int dp[110][110];
int strangePrinter(string s) {
//dp[i][j] 打印i --- j 所需要的步数
memset(dp, 0x3f, sizeof(dp));
int n = s.size();
for(int i = 0; i < n; i ++ ) dp[i][i] = 1;//打印一个字符用i
for(int len = 2; len <= n; len ++ )
{
for(int i = 0; i + len - 1 < n; i ++ )
{
int j = i + len - 1;
if(s[i] == s[j]) dp[i][j] = dp[i][j - 1];
else
for(int k = i; k < j; k ++ )//先打印到k再打印到j
{
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
}
return dp[0][n - 1];
}
};
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int length = piles.size();
auto dp = vector<vector<int>>(length, vector<int>(length));
for (int i = 0; i < length; i++) {
dp[i][i] = piles[i];
}
for (int i = length - 2; i >= 0; i--) {
for (int j = i + 1; j < length; j++) {
dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);//可以取走i或者j,剩下的人再从剩余堆中取,他们的差是当前玩家与另一个玩家的得分差
}
}
return dp[0][length - 1] > 0;
}
};
class Solution {
public:
int countArrangement(int n) {
vector<int> f(1 << n);
f[0] = 1;
for (int mask = 1; mask < (1 << n); mask++) {
int num = __builtin_popcount(mask);//放到哪里
for (int i = 0; i < n; i++) {//那个数要放
if (mask & (1 << i) && (num % (i + 1) == 0 || (i + 1) % num == 0)) {
f[mask] += f[mask ^ (1 << i)];//不放这个数的方案
}
}
}
return f[(1 << n) - 1];
}
};
class Solution {
public:
static const int N = 33;
int f[N][2];
void init()
{
//处理一位的情况
f[1][0] = 1;
f[1][1] = 1;
//从第二位开始处理
for(int i = 2; i < N; i ++ )
{
f[i][0] = f[i - 1][0] + f[i - 1][1];// 这一位是0 ,上一位1或者0
f[i][1] = f[i - 1][0];// 这一位是1, 上一位必是 0
}
}
int dp(int n)
{
if(!n) return 1;
int res = 0;
vector<int> nums;//将n的各位数字摘出来
while(n) nums.push_back(n % 2), n /= 2;
int last = 0;//记录n的上一位
for(int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
//可以有前导,因为前导0不会导致数量减少
if(x == 1)//累加这一位是0--->x-1的情况,此时他的低位可以随便选,
res += f[i + 1][0];
if(last && x)break;// 判断这一位是x是否成立,成立的话可以继续遍历
else last = x;
if(!i) res ++; //n遍历到最后一位依然成立,这时会有一的累加
}
// 有前导0的部分
// for(int i = 1; i < nums.size(); i ++ )
// res += f[i][1];//前边有0,后边可以任意取。
// res += f[1][0]; // 全是0
return res;
}
int findIntegers(int n) {
init();//初始化每一位没有大小限制的答案数,此时可以有前导0,在进行dp的时候保证没有前导0即可。
return dp(n);
}
};