1. 森林中的兔子 - 贪心
森林中有未知数量的兔子。提问其中若干只兔子 “还有多少只兔子与你(指被提问的兔子)颜色相同?” ,将答案收集到一个整数数组 answers
中,其中 answers[i]
是第 i
只兔子的回答。
给你数组 answers
,返回森林中兔子的最少数量。
Example 1
输入:answers = [1,1,2]
输出:5
解释:
两只回答了 "1" 的兔子可能有相同的颜色,设为红色。
之后回答了 "2" 的兔子不会是红色,否则他们的回答会相互矛盾。
设回答了 "2" 的兔子为蓝色。
此外,森林中还应有另外 2 只蓝色兔子的回答没有包含在数组中。
因此森林中兔子的最少数量是 5 只:3 只回答的和 2 只没有回答的。
Example 2
输入:answers = [10,10,10]
输出:11
Code
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int main() {
ios::sync_with_stdio(false);
unordered_map<int, int> mp;
int n, t, ans = 0;
cin >> n;
while (n--) {
cin >> t;
++mp[t];
}
for (auto& [x, y]:mp) {
ans += (x + y) / (x + 1) * (x + 1);
}
cout << ans;
return 0;
}
2. 最长有效括号串 - 栈
小明的老师给小明布置了一道作业,作业内容如下:
给定一个只包含 '('
和 ')'
的字符串,找出最长的包含有效括号的子串的长度。
由于小明太忙了,所以想请你帮忙完成,聪明的你可以帮小明解决这个问题吗?
有效括号串定义为左右可以匹配,例如'('
和')'
可以匹配。
匹配规则如下:
(1)若a
是有效括号串,则(a)
也是;
(2)若a
、b
是有效括号串,则ab
也是。
Example 1
输入:)()())
输出:4
Example 2
输入:)(()())())(()())
输出:8
Code 1 [低效率代码]
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int main() {
ios::sync_with_stdio(false);
string s;
cin >> s;
stack<int> st; // idx
unordered_map<int, int> mp; // [end, cnt]
int maxLength = 0, curLength, prevLength;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '(') {
st.push(i);
} else { // s[i] == ')'
if (!st.empty() && s[st.top()] == '(') { // match
curLength = i + 1 - st.top();
prevLength = mp.count(st.top() - 1) ? mp[st.top() - 1] : 0;
maxLength = max(maxLength, curLength + prevLength);
mp[i] = curLength + prevLength;
st.pop();
} else { // no match
st.push(i);
}
}
}
cout << maxLength;
return 0;
}
Code 2
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int main() {
ios::sync_with_stdio(false);
string s;
cin >> s;
stack<int> st; // idx
st.push(-1);
int maxLength = 0;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == ')' && !st.empty() && s[st.top()] == '(') {
st.pop();
maxLength = max(maxLength, i - st.top());
} else {
st.push(i);
}
}
cout << maxLength;
return 0;
}
3. 小明和二分图 - DFS
小明最近在学习二分图,由于小明现在还是初级阶段,什么都不会,这天小明的老师给小明这样一道题目,题目描述如下。给定一个图,判断这个图是不是二分图。小明看到这个问题犯了难,聪明的你可以帮助小明解决这个问题吗?
(这里给出一个二分图的通俗定义:顶点集 V 可分割为两个互不相交的子集 A,B,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻,满足这样条件的图叫二分图。即:点集 A 或点集 B 内部不存在边,所有边都是 A 连到 B)
输入
本题包括多组测试数据(不超过20组) 第一行输入一个t,表示测试数据的数目; 对于每个测试数据,第一行输入两个数字n和m,表示图点的个数和边的个数; 接下来m行,每行两个数字u和v,表示点u和点v之间存在一条边。(1<= n<=500,1<=m<=70000)
Data Description: 对于10%的数据,1<= n<= 4; 对于50%的数据,1<= n<= 100; 对于100%的数据,1<= n<=500,1<=m<=70000。
输出
输出t行,对于每个测试数据,如果这个图是二分图输出“YES”,否则输出“NO”。
输入样例
2
4 4
1 3
1 4
2 3
2 4
3 3
1 2
1 3
2 3
输出样例
YES
NO
代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
enum {
RED, BLUE, UNCOLORED
};
bool dfs(const vector<vector<int>> &graph, vector<int> &status, int color, int node) {
status[node] = color;
int nextColor = (color == RED) ? BLUE : RED;
for (int nextNode:graph[node]) {
if (status[nextNode] == color) return false;
if (status[nextNode] == nextColor) continue;
if (!dfs(graph, status, nextColor, nextNode)) return false;
}
return true;
}
bool solve(int n, int m) {
vector<vector<int>> graph(n + 1);
vector<int> status(n + 1, UNCOLORED);
int x, y;
while (m--) {
cin >> x >> y;
graph[x].push_back(y);
graph[y].push_back(x);
}
for (int node = 1; node <= n; node++) {
if (status[node] == UNCOLORED) {
if (!dfs(graph, status, RED, node)) return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
int t, n, m;
cin >> t;
while (t--) {
cin >> n >> m;
cout << (solve(n, m) ? "YES" : "NO") << endl;
}
return 0;
}
4. 完全背包问题
有N
种物品和一个容量为V
的背包,每种物品都有无限件可用。第i
种物品的体积是v[i]
,价值是c[i]
。
现在请你选取一些物品装入背包,使这些物品的体积总和不超过背包容量,且价值总和最大。
其中1<=N<=100
,1<=V<=50000
,1<=v[i],c[i]<=10000
。
输入
第一行输入两个数N,V,分别表示物品种类数和背包容积; 之后N行,每行两个数v[i],c[i],分别表示第i种物品的体积和价值;
输出
输出一个数,表示最大的价值
输入样例
2 11
2 3
6 14
输出样例
20
代码 1 [超时]
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int weight[105], value[105], dp[50005];
int main() {
ios::sync_with_stdio(false);
int n, maxWeight;
cin >> n >> maxWeight;
for (int i = 1; i <= n; ++i) { cin >> weight[i] >> value[i]; }
for (int i = 1; i <= n; ++i) {
for (int s = maxWeight; s > 0; --s) {
for (int k = 1; k <= s / weight[i]; ++k) {
dp[s] = max(dp[s], dp[s - k * weight[i]] + value[i] * k);
}
}
}
cout << dp[maxWeight];
return 0;
}
代码 2
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int weight[105], value[105], dp[50005];
int main() {
ios::sync_with_stdio(false);
int n, maxWeight;
cin >> n >> maxWeight;
for (int i = 1; i <= n; ++i) { cin >> weight[i] >> value[i]; }
for (int i = 1; i <= n; ++i) {
for (int s = weight[i]; s <= maxWeight; ++s) {
dp[s] = max(dp[s], dp[s - weight[i]] + value[i]);
}
}
cout << dp[maxWeight];
return 0;
}
5. 数独 - 回溯
数独游戏规则如下:在9 * 9的盘面上有些已知的数字及未知的数字,推理出所有未知的数字,并满足每一行、每一列、每一个粗线宫内的数字均含1-9,不重复。
有些局面存在多个解或无解,这属于不标准的数独。对于不标准的局面,输出No Solution。
输入
第1 - 9行,每行9个数中间用空格分隔,0表示该格子的数未知。
输出
如果局面是不标准的,输出No Solution,否则数据具体的解。
输入样例
0 6 0 5 9 3 0 0 0
9 0 1 0 0 0 5 0 0
0 3 0 4 0 0 0 9 0
1 0 8 0 2 0 0 0 4
4 0 0 3 0 9 0 0 1
2 0 0 0 1 0 6 0 9
0 8 0 0 0 6 0 2 0
0 0 4 0 0 0 8 0 7
0 0 0 7 8 5 0 1 0
输出样例
7 6 2 5 9 3 1 4 8
9 4 1 2 7 8 5 3 6
8 3 5 4 6 1 7 9 2
1 9 8 6 2 7 3 5 4
4 7 6 3 5 9 2 8 1
2 5 3 8 1 4 6 7 9
3 8 7 1 4 6 9 2 5
5 1 4 9 3 2 8 6 7
6 2 9 7 8 5 4 1 3
代码 1 [不考虑存在多解情况]
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int grid[9][9];
bool check(int x, int y, int val) {
for (int i = 0; i < 9; ++i) {
if (grid[x][i] == val || grid[i][y] == val) return false;
}
int r = x / 3 * 3, c = y / 3 * 3;
for (int i = r; i < r + 3; ++i) {
for (int j = c; j < c + 3; ++j) {
if (grid[i][j] == val) return false;
}
}
return true;
}
bool backtracking() {
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (grid[i][j] != 0) continue;
for (int val = 1; val <= 9; ++val) {
if (check(i, j, val)) {
grid[i][j] = val;
if (backtracking()) return true;
grid[i][j] = 0;
}
}
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
cin >> grid[i][j];
}
}
if (backtracking()) {
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
cout << grid[i][j] << ' ';
}
cout << endl;
}
} else {
cout << "No Solution" << endl;
}
return 0;
}
代码 2
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int grid[9][9], result[9][9];
int cnt = 0;
bool check(int x, int y, int val) {
for (int i = 0; i < 9; ++i) {
if (grid[x][i] == val || grid[i][y] == val) return false;
}
int r = x / 3 * 3, c = y / 3 * 3;
for (int i = r; i < r + 3; ++i) {
for (int j = c; j < c + 3; ++j) {
if (grid[i][j] == val) return false;
}
}
return true;
}
void backtracking() {
if (cnt > 1) return; // 发现多解, 退出函数
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (grid[i][j] != 0) continue;
for (int val = 1; val <= 9; ++val) {
if (check(i, j, val)) {
grid[i][j] = val;
backtracking();
grid[i][j] = 0;
}
}
return; // 所有情况考虑完毕, 退出函数
}
}
++cnt; // 找到可行解, 可行解数量 +1
for (int i = 0; i < 9; ++i) { // 保存答案
for (int j = 0; j < 9; ++j) {
result[i][j] = grid[i][j];
}
}
}
int main() {
ios::sync_with_stdio(false);
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
cin >> grid[i][j];
}
}
backtracking();
if (cnt == 1) {
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
cout << result[i][j] << ' ';
}
cout << endl;
}
} else {
cout << "No Solution" << endl;
}
return 0;
}
6. 小陶与杠铃片 - 线段树
小陶在举重队负责后勤工作。举重队的训练场中有一个区域一排码放了n
片杠铃片,每天运动员们训练完之后会将杠铃片放回,之后小陶需要重新整理杠铃片的顺序,使它们由轻到重依次排好。由于杠铃片很重,小陶每次只能选两片相邻的杠铃片,交换它们的位置。现在小陶想知道,这一天他至少需要交换多少次才能整理完毕?
已知n<=200000
,0<=杠铃片重量<=200000
。
输入
第一行一个正整数n,表示有n片杠铃片;
第二行n个整数,表示运动员们放回后每片杠铃片依次的重量。
输出
输出一个整数,表示小陶至少交换的次数。
输入样例
10
16808 75250 50074 143659 108931 11273 27545 50879 177924 37710
输出样例
20
代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int segTree[800080];
void insert(int start, int end, int node, int val) {
segTree[node]++;
if (start == end) return;
int mid = start + ((end - start) >> 1);
int left_node = (node << 1) + 1, right_node = (node << 1) + 2;
val <= mid ? insert(start, mid, left_node, val) : insert(mid + 1, end, right_node, val);
}
int query(int start, int end, int node, int L, int R) {
if (R < start || L > end) return 0;
if (L <= start && end <= R) return segTree[node];
int mid = start + ((end - start) >> 1);
int left_node = (node << 1) + 1, right_node = (node << 1) + 2;
return query(start, mid, left_node, L, R) + query(mid + 1, end, right_node, L, R);
}
int main() {
ios::sync_with_stdio(false);
int n, val;
long long ans = 0ll;
cin >> n;
while (n--) {
cin >> val;
ans += query(0, 200000, 0, val + 1, 200000);
insert(0, 200000, 0, val);
}
cout << ans;
return 0;
}