int(){ cin >> n >> m; for (int i = 1; i <= m; i++) f[i] = -INF; for (int i = 0; i < n; i++) { cin >> v >> w; for (int j = m; j >= v; j--) f[j] = max (f[j], f[j - v] + w); } int maxw = 0; for (int i = 0; i <= m; i++) maxw = max (maxw, f[i]); cout << maxw << endl; return0; }
完全背包
f[i] 表示 总体积是i的情况下, 最大价值是多少。
result = max{f[0 … m]} // m 是 最大体积
for (int i = 0; i < n; i++) { for (int j = v[i]; j <= m; j++ ) f[j] = max (f[j], f[j - v[i]] + w[i]); }
int(){ cin >> n >> m; for (int i = 0; i < n; i++) { //循环1. 物品 int v, w, s; cin >> v >> w >> s; for (int j = m; j >= v; j--) //循环2 背包可用体积 for (int k = 1; k <= s && k*v <= j; k++) //循环3 每种物品的数量(后面二进制优化不要在这里进行) f[j] = max(f[j], f[j - k*v] + k*w); }
cout << f[m] << endl;
return0; }
多重背包的二进制优化方法
数据范围 0 < N <= 1000 0 < V <= 2000 0 < vi, wi, si <= 2000
intmain(){ vector<Good> goods; cin >> n >> m; //划分物品 for (int i = 0; i < n; i++) { int v, w, s; cin >> v >> w >> s; for (int k = 1; k <= s; k *= 2) { s -= k; goods.push_back({ v * k, w * k }); } if (s > 0) goods.push_back({ v * s, w * s }); } // 01 背包 for (auto good : goods) for (int j = m; j >= good.v; j--) f[j] = max(f[j], f[j - good.v] + good.w);
structThing { int kind; int v, w; }; vector<Thing> things;
intmain(){ cin >> n >> m; for (int i = 0; i < n; i++) { int v, w, s; cin >> v >> w >> s; if (s < 0) things.push_back({ -1, v, w }); elseif (s == 0) things.push_back({ 0, v, w }); else { for (int k = 1; k <= s; k *= 2) { s -= k; things.push_back({ -1, v * k, w * k }); } if (s > 0) { things.push_back({ -1, v * s, w * s }); } } }
for (auto thing : things) { if (thing.kind < 0) { //01背包 (多重背包已转换成01背包) for (int j = m; j >= thing.v; j--) f[j] = max(f[j], f[j - thing.v] + thing.w); } else { //完全背包 for (int j = thing.v; j <= m; j++) f[j] = max(f[j], f[j - thing.v] + thing.w); } }
intmain(){ cin >> n >> v >> m; for (int i = 0; i < n; i++) { int a, b, c; cin >> a >> b >> c; for (int j = v; j >= a; j--) for (int k = m; k >= b; k--) f[j][k] = max(f[j][k], f[j - a][k - b] + c); }
int n, m; int s; int f[N], v[N], w[N]; /* 与01背包类似。 每组物品至多选一个,因此每组有s+1种决策(输入时要把该组物品一次性输入,因此需要用数组来存储) f[j] = max {f[j], f[j- v[0] + w[0], f[j - v[1]] + w[1], f[j - v[2]] + w[2],... f[j - v[s - 1]] + w[s - 1]} */
intmain(){ cin >> n >> m; for (int i = 0; i < n; i++) { cin >> s; for (int i = 0; i < s; i++) cin >> v[i] >> w[i]; for (int j = m; j >= 0; j--) for (int k = 0; k < s; k++) if (j >= v[k]) f[j] = max(f[j], f[j - v[k]] + w[k]); }
constint N = 1010, mod = 1000000007, INF = 1000000;
int n, m; int f[N], g[N];
intmain(){ cin >> n >> m; g[0] = 1; for (int i = 1; i <= m; i++) f[i] = -INF; for (int i = 0; i < n; i++) { int v, w; cin >> v >> w; for (int j = m; j >= v; j--) { int t = max (f[j], f[j - v] + w); int s = 0; if (t == f[j]) s += g[j]; if (t == f[j - v] + w) s += g[j - v]; if (s >= mod) s -= mod; f[j] = t; g[j] = s; } }
int maxw = 0; for (int i = 0; i <= m; i++) maxw = max (maxw, f[i]); int res = 0; for (int i = 0; i <= m; i++) if (maxw == f[i]) { res += g[i]; if (res >= mod) res -= mod; } cout << res << endl; return0; }
voidmerge_sort(vector<int>& q, int l, int r){ if(l == r) return; // l >= r 也可以
int mid = (l + r) / 2; // l + r >> 1 也可以 merge_sort(q, l, mid); //先排序左半边, 再排序右半边 merge_sort(q, mid + 1, r);
staticvector<int> w; w.clear();
int i = l, j = mid + 1;
while (i <= mid && j <= r) if (q[i] <= q[j]) // 这里注意 一定是 <= 才行 w.push_back(q[i++]); else//else 很精髓 w.push_back(q[j++]); while (i <= mid) w.push_back(q[i++]); while (j <= r) w.push_back(q[j++]); //把两个有序序列合并成了一个有序序列 //注意下面的循环判断条件哪里, 用的是 j for (i = l, j = 0; j < w.size(); i++, j++) q[i] = w[j]; // i从原序列的最左边开始, j从辅助数组的最左边开始。 }
快速排序
1 2 3 4 5 6 7 8 9 10
voidquick_sort(vector<int>& nums, int l, int r){ //r是数组最后一个元素的下标 if (l >= r) return; // 这里是 == 也行 int i = l - 1, j = r + 1, x = nums[l + r >> 1]; // 这里pivot就选择是 mid 了 while (i < j) { // 使用 do while 循环 很精髓, 如果是用的while ,先判断再移动指针,当nums[i] == x 或 nums[j] == x 时 就会陷入死循环 ~ do j--; while (nums[j] > x); // do while 先执行 后判断 do i++; while (nums[i] < x); if (i < j) swap(nums[i], nums[j]); else quick_sort(nums, l, j), quick_sort(nums, j + 1, r); //把递归写在循环外面也是可以的 } }
1 2 3 4 5 6 7 8 9 10 11 12 13
voidquick_sort(vector<int>& nums, int l, int r){ if (l >= r) return; // 这里是 == 也行 int i = l - 1, j = r + 1, x = nums[l + r >> 1]; while (i < j) { do j--; while (nums[j] > x); // do while 先执行 后判断 do i++; while (nums[i] < x); if (i < j) swap(nums[i], nums[j]); //i < j i、j还没有相遇; 否则 i、j已经相遇, 即 比x小的已经放在了左边, 比x大的已经放在了右边 } quick_sort(nums, l, j); quick_sort(nums, j + 1, r); }
do while 语句
1 2 3
do { 我上传题目; } while (系统判断是否正确); // 注意循环操作 和 循环条件 后面都要跟上 分号
快速排序找数组中第k小的数字
每轮排序都有一个pivot就位, 判断一下这个pivot的下标是否为 k - 1, 如果是那么它就是第k小的数字, 否则向左/右递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
intquick_sort(vector<int>& nums, int l, int r, int k){ if (l >= r) return nums[l]; int i = l - 1, j = r + 1, x = nums[l]; while (i < j) { do i++; while (nums[i] < x); do j--; while (nums[j] > x); if (i < j) swap(nums[i], nums[j]); } if (j == k - 1) return nums[j]; elseif (j > k - 1) quick_sort(nums, l, j - 1, k); else quick_sort(nums, j + 1, r, k);
voidbubble(vector<int>& nums){ int n = nums.size(); bool flag = false; for (int i = n - 1; i > 0; i--) { for (int j = 0; j + 1 <= i; j++) { if (nums[j] > nums[j + 1]) swap(nums[j], nums[j + 1]); flag = true; } if (!flag) break; }
} voidselection_sort(vector<int> & nums){ int n = nums.size(); for (int i = 0; i < n; i++) //i前面的数字已经排好序了 for (int j = i + 1; j < n; j++) if (nums[i] > nums[j]) //把最小元素放在第一位 swap(nums[i], nums[j]); } voidinsertion_sort(vector<int> & nums){ int n = nums.size();
for (int i = 1; i < n; i++) { int temp = nums[i], j; for (j = i - 1; j >= 0; j--) { if (nums[j] > temp) nums[j + 1] = nums[j]; // 注意是nums[j + 1] 而不是 nums[i], 不要短视 else break; //这个break 是精髓 } nums[j + 1] = temp; } }
voidquick_sort(vector<int>& nums, int l, int r){ if (l == r) return; // >= 也可 int i = l - 1, j = r + 1, x = nums[l + r >> 1]; while (i < j) { //while循环, 确保安置好一个pivot后 再 递归 左右。 do j--; while (nums[j] > x); // do while 先执行 后判断 do i++; while (nums[i] < x); if (i < j) swap(nums[i], nums[j]); } quick_sort(nums, l, j); quick_sort(nums, j + 1, r); }
voidpush_down(vector<int>& heap, int size, int u){ //这里size是必不可少的 int t = u; // t是当前的最大值(根节点和lc , rc之间的) int left = u * 2, right = u * 2 + 1; if (left <= size && heap[left] > heap[t]) t = left; // 如果左儿子存在, 并且.. if (right <= size && heap[right] > heap[t]) t = right;//如果右儿子存在, 并且.. if (t != u) {//说明某个儿子的值 比 我大 了 swap(heap[u], heap[t]); push_down(heap, size, t); } } voidpush_up(vector<int>& heap, int u){ while (u / 2 && heap[u / 2] < heap[u]) { // u / 2 就是 当前节点父节点的编号 swap(heap[u / 2], heap[u]); u /= 2; } }
voidinsert(vector<int>& heap, int size, int x){ heap[++size] = x; // 插入一个元素, 把它放到最后面即可 push_up(heap, x); }
voidheap_sort(vector<int>& nums, int size){ int n = size; for (int i = 1; i <= n; i++) push_up(nums, i); //建堆 for (int i = 1; i <= n; i++) { //注意这里 控制条件 是 n ,与后面nums的下标不能是一个变量 swap(nums[1], nums[size]); // 把最大值放到最后 size--; push_down(nums, size, 1); } } // 给定10^7 个数, 每个数都在 0~10^6之间 voidcounting_sort(vector<int>& nums, int n){ vector<int> cnt(101, 0); for (int i = 1; i <= n; i++) cnt[nums[i]]++;
for (int i = 1, k = 1; i <= 100; i++) //注意这里设置的 k while (cnt[i]) { nums[k++] = i; //注意这里的 k, i cnt[i]--; } }
voidbucket_sort(vector<int>& nums){
}
intget(int x, int i){ while (i--) x /= 10; return x % 10; } // 假设所有数字 在三位数以内 // 由于上面用过堆排序...所以这里的输入是从1 开始的, 可自行更改 voidradix_sort(vector<int>& nums, int n){ //特别要注意后面索引用的是i 还是 j 还是 k... vector<vector<int>> cnt(10); // 定义十个桶 for (int i = 0; i < 3; i++) { for (int j = 0; j < 10; j++) cnt[j].clear();
for (int j = 1; j <= n; j++) // 按个位排序 cnt[get(nums[j], i)].push_back(nums[j]); for (int j = 0, k = 1; j < 10; j++) for (int x : cnt[j]) // 这里用这个 range for 就很聪明了 nums[k++] = x; } }
intmain(){ int n, t; vector<int> nums; cin >> n; nums.resize(n + 1); //记一下这个函数 for (int i = 1; i <= n; i++) cin >> nums[i]; // 堆 下标从1 开始 /*for (int i = 0; i < n; i++) { cin >> t; nums.push_back(t); } */ //bubble(nums); //selection_sort(nums); //insertion_sort(nums); //merge_sort(nums, 0, nums.size() - 1); //quick_sort(nums, 0, nums.size() - 1); //heap_sort(nums, n); //counting_sort(nums, n); radix_sort(nums, n); /* for (auto x : nums) cout << x << ' '; */ for (int i = 1; i <= n; i++) cout << nums[i] << ' '; return0; }
//for (int x : nums) cout << x << " "; cout << endl; }
剑指offer
不修改数组找出重复的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution { public: intduplicateInArray(vector<int>& nums){ int l = 1, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; // 划分的区间:[l, mid], [mid + 1, r] int s = 0; for (auto x : nums) s += x >= l && x <= mid; if (s > mid - l + 1) r = mid; else l = mid + 1; } return r; } };
classSolution { public: boolhasPath(vector<vector<char>>& matrix, string str){ for (int i = 0; i < matrix.size(); i++) for (int j = 0; j < matrix[0].size(); j++) if(dfs (matrix, str, 0, i, j)) returntrue; returnfalse; } booldfs(vector<vector<char>>& matrix, string& str, int u, int x, int y){ if (matrix[x][y] != str[u]) returnfalse; if (u == str.size() - 1) returntrue;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //上、右、下、左 char t = matrix[x][y]; matrix[x][y] = '*'; for (int i = 0; i < 4; i++) { int a = x + dx[i], b = y + dy[i]; if (a >= 0 && a < matrix.size() && b >= 0 && b < matrix[0].size()) if (dfs(matrix, str, u + 1, a, b)) returntrue; } matrix[x][y] = t; //这里记得要回溯 returnfalse; } };
旋转数组的最小数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intfindMin(vector<int>& nums){ int n = nums.size() - 1; if (n < 0) return-1; while (n > 0 && nums[n] == nums[0]) n--; if (nums[n] >= nums[0]) return nums[0]; int l = 0, r = n; while (l < r) { int mid = l + r >> 1; if (nums[mid] < nums[0]) r = mid; else l = mid + 1; } return nums[r]; } };
intget_sum(pair<int, int> p){ int s = 0; while (p.first) { s += p.first % 10; p.first /= 10; } while (p.second) { s += p.second % 10; p.second /= 10; } return s; }
intmovingCount(int threshold, int rows, int cols) { if (!rows || !cols) return0; queue<pair<int,int>> q; vector<vector<bool>> st(rows, vector<bool>(cols, false));
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int res = 0; q.push({0, 0}); while (q.size()) { auto t = q.front(); q.pop(); if (st[t.first][t.second] || get_sum(t) > threshold) continue; //直接进行下一次循环 res ++ ; //这个点可以选择 st[t.first][t.second] = true; //标记一下,这个点走过了 for (int i = 0; i < 4; i ++ ) { //向 上、右、下、左 四个方向扩展 int x = t.first + dx[i], y = t.second + dy[i]; if (x >= 0 && x < rows && y >= 0 && y < cols) //因为已经判断过第一个if中的两个条件,所以此处只需要判断是否在界内即可。 q.push({x, y}); } }
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ //倒数第k个节点 即 正数第 len - k + 1个节点 。 classSolution { public: ListNode* findKthToTail(ListNode* pListHead, int k){ int len = 0; auto p = pListHead; while (p != nullptr) { p = p->next; len++; } if (k > len) returnnullptr; p = pListHead; for (int i = 0; i < len - k; i++) p = p->next; //往后挪len - k 次 return p; } };
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ classSolution { public:
// Encodes a tree to a single string. stringserialize(TreeNode* root){ string res; dfs_s (root, res); return res; } voiddfs_s(TreeNode* root, string& res){ if (!root) { res += "null "; // 这里后面有个空格 return; } res += to_string(root->val) + ' '; dfs_s(root->left, res); dfs_s(root->right, res); }
// Decodes your encoded data to tree. TreeNode* deserialize(string data){ int u = 0; return dfs_d (data, u); } TreeNode* dfs_d(string data, int& u){ if (u == data.size()) returnNULL; int k = u; while (data[k] != ' ') k++; if (data[u] == 'n') { u = k + 1; returnNULL; } //考虑了负号 int val = 0; if (data[u] == '-') { u = u + 1; for (int i = u; i < k; i++) val = val * 10 + data[i] - '0'; val = -val; } else { for (int i = u; i < k; i++) val = val * 10 + data[i] - '0'; //注意这里的操作 } u = k + 1; auto root = new TreeNode(val); root->left = dfs_d(data, u); //此处,因为data是VLR的顺序, 所以左边的填完,就是右边的了。 root->right = dfs_d(data, u); return root; } };
int a = 11; // 二进制1011 , cout << (a >> 2 & 1) << endl; //输出0 cout << (a >> 1 & 1) << endl; // 输出1
关于DFS与backtracing
I would say, DFS is the special form of backtracking; backtracking is the general form of DFS.
If we extend DFS to general problems, we can call it backtracking. If we use backtracking to tree/graph related problems, we can call it DFS.
They carry the same idea in algorithmic aspect.
I think this answer to another related question offers more insights.
For me, the difference between backtracking and DFS is that backtracking handles an implicit tree and DFS deals with an explicit one. This seems trivial, but it means a lot. When the search space of a problem is visited by backtracking, the implicit tree gets traversed and pruned in the middle of it. Yet for DFS, the tree/graph it deals with is explicitly constructed and unacceptable cases have already been thrown, i.e. pruned, away before any search is done.
So, backtracking is DFS for implicit tree, while DFS is backtracking without pruning.
最小的k个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution { public: vector<int> getLeastNumbers_Solution(vector<int> input, int k) { priority_queue<int> heap; vector<int> res; for (auto x : input) { heap.push(x); if (heap.size() > k) heap.pop(); } while (k--) { res.push_back(heap.top()); heap.pop(); } // vector<int> ans(res.rbegin(), res.rend()); // return ans; reverse(res.begin(), res.end()); return res; } };
classSolution { public: intmaxSubArray(vector<int>& nums){ // s表示以前一个数为结尾的子数组中,和最大的值 int res = INT_MIN, s = 0; //因为可能数组全为负数,因此把res初始化为负无穷。 for (auto x : nums) { if (s < 0) s = 0; s += x; res = max (res, s); } return res; } };
classSolution { public: intgetNumberOfK(vector<int>& nums , int k){ if (!nums.size()) return0; int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] < k) l = mid + 1; //nums[mid] < k, 也就是说mid不是解, 右半边是解在的区间, mid在左半边, 选择模板1 else r = mid; } if (nums[l] != k) return0; // 如果k不是nums数组中的数。 int left = l; l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r + 1 >> 1; if (nums[mid] > k) r = mid - 1; // nums[mid] > k, 也就是说 mid 不是解, 左半边是解在的区间, mid在右半边, 选择模板2 else l = mid ; } return l - left + 1; } };
classSolution { public: intgetMissingNumber(vector<int>& nums){ int n = nums.size() + 1; // 这里注意一下 + 1 int res = n * (n - 1) / 2; for (auto x : nums) res -= x; return res; } };
数组中数值和下标相等的元素
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution { public: intgetNumberSameAsIndex(vector<int>& nums){ int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] - mid >= 0) r = mid; else l = mid + 1; } if (nums[r] == r) return r; return-1; } };
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ classSolution { public: boolisBalanced(TreeNode* root){ if (!root) returntrue; int a = abs(depth(root->left) - depth(root->right)); if (a > 1) returnfalse; return isBalanced (root->left) && isBalanced (root->right); } intdepth(TreeNode* root){ if (!root) return0; return max (depth(root->left), depth(root->right)) + 1; } };
这个题注意 必须 每一层的节点都平衡 才是 平衡二叉树。
数组中只出现一次的两个数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: vector<int> findNumsAppearOnce(vector<int>& nums) { int sum = 0; for (auto x : nums) sum ^= x; // a^b int k = 0; while (!(sum >> k & 1)) k++; //a, b第k位不相同 int first = 0; for (auto x : nums) if (x >> k & 1) first ^= x; int second = sum ^ first; returnvector<int> {first, second}; // 花括号初始化 } };
异或
相同的数异或 为 0
不同的数异或为 1 1^1 = 0 1^0 = 1
解法步骤 :
把 所有数 异或, 得到 x^y;
x^y的第k位是1
对于每个数a 看他的第k位 是否为 1, 把是0的归为一个集合, 是1 的归为一个集合
反转单词顺序
首先我们看看怎么反转整个字符串, 用两个指针就可以了。
1 2 3 4 5
string s = "Harry potter!"; for (int i = 0, j = s.size() - 1; i < j; i++, j--) swap(s[i], s[j]);
cout << s << endl;
用操作分解的角度来做这题。 1.反转整个句子 2.反转每个单词
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution { public: stringreverseWords(string s){ reverse(s.begin(), s.end()); for (int i = 0; i < s.size(); i++) { int j = i; while (j < s.size() && s[j] != ' ') j++; reverse(s.begin() + i, s.begin() + j); //[first, second) i = j; } return s; } };
classSolution { public: stringleftRotateString(string str, int n){ int k = str.size(); reverse(str.begin(), str.end()); reverse(str.begin(), str.begin() + k - n); reverse(str.begin() + k - n, str.end()); return str; } };
反转整个序列 gfedcba 分别反转前后两个序列 cdefgab
滑动窗口的最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: //要不要加1 减1 可以 代入一个特殊值来判断 vector<int> maxInWindows(vector<int>& nums, int k) { vector<int> res; deque<int> q; //双端队列里存放的是下标 for (int i = 0; i < nums.size(); i++) { while (q.size() && q.front() <= i -k) q.pop_front(); //将滑出窗口的元素弹出(判断队首元素是否需要出队) while (q.size() && nums[q.back()] <= nums[i]) q.pop_back();//维护队列单调性。如果队尾元素比新插入的那个小,那么它将永无出头之日... q.push_back(i); //把这个元素插入deque if (i >= k - 1) res.push_back(nums[q.front()]); } return res; } };
#include<list> classSolution { public: intlastRemaining(int n, int m){ list<int> nums; for (int i = 0; i < n; i++) nums.push_back(i); auto it = nums.begin(); int k = m - 1; while (nums.size() > 1) { while (k--) { it++; if (it == nums.end()) it = nums.begin(); //把迭代器移到开头模拟环形列表 } it = nums.erase(it); //删除第m个元素 if (it == nums.end()) it = nums.begin(); k = m - 1; } return nums.front(); } };
##
1 2 3 4 5 6 7 8
classSolution { public: intgetSum(int n){ int res = n; n > 0 && (res += getSum(n - 1)); //短路操作, 当 n>0 时, 才会执行后面的操作。 return res; } };
intmain() { int n; cin >> n; //表示有n个数据 vector<string> ans; for (int i = 0; i < n; i++) { int num; cin >> num; int j = 0; int temp = num; vector<int> t;
while (temp) { //转换成二进制 t.push_back(temp % 2); temp /= 2; } int sum = 0; for (int j = 0; j < t.size(); j++) { sum += t[j] * pow(2, t.size() - j - 1); }
if (sum - num == 0) ans.push_back("Yes"); else ans.push_back("No"); }
近期评论