1. 括号之价 [栈]
小Y
上数据结构课的时候摸鱼,听到老师在讲用栈做括号匹配,于是乎边随意写了一个合法的括号序列。但是光是写括号太无聊了,他现在想知道这个括号序列的价值。他是这样定义一个括号序列的价值的:
1、
一对括号价值一分(比如"()"
得一分)
2、
两个合法的括号序列的拼接而成的括号序列的价值是他们的价值的和(比如"()()"
价值为1+1=2
)
3、
嵌套的括号的序列的价值是,所嵌套的括号序列的价值的翻倍(比如"((()))"
价值为1*2*2=4
)
下课了,qz
看到小Y
写的括号序列,他一眼就推测出了规则并得到了括号序列的价值。那么问题来了,小Y
写下的括号序列的价值是多少呢?
输入
一个只包含'('和')'的合法的括号序列S,代表小Y写下的括号序列,一个合法的括号序列是这样定义的:
1、()是合法的括号序列
2、若字符串A和B是合法的括号序列,那么AB也是合法的括号序列
3、若字符串A是合法的括号序列,那么(A)也是合法的括号序列
2<= |S| <=50
输出
一个字符串S,代表小Y所写的括号序列
输入样例
(()(()))
输出样例
6
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
stack<tuple<int, int, int>> scores; // start, end, score
stack<int> st;
for (int i = 0, prev; i < s.size(); ++i) {
if (s[i] == '(') {
st.push(i);
continue;
}
if (!scores.empty() && st.top() == get<0>(scores.top()) - 1) { // s[i] == ')' - double
auto[start, end, score]=scores.top();
scores.pop();
st.pop();
scores.push({start - 1, end + 1, score * 2});
} else {
prev = st.top();
st.pop();
scores.push({prev, i, 1});
}
if (scores.size() >= 2) { // add
auto[start, end, score]=scores.top();
scores.pop();
if (start == get<1>(scores.top()) + 1) {
auto[start_, end_, score_]=scores.top();
scores.pop();
scores.push({start_, end, score + score_});
} else {
scores.push({start, end, score});
}
}
}
cout << get<2>(scores.top());
return 0;
}
代码 [author: G_bg
]
/**
* @Author: G_bg
* @DateTime: 2020-02-11 15:58:39
*/
#include<bits/stdc++.h>
using namespace std;
int a[55], num[55], ans[55];
int main() {
string str;
cin >> str;
int len = str.size(), tmp = 0;
for (int i = 0; i < len; ++i) {
str[i] == '(' ? a[i] = ++tmp : a[i] = tmp--;
}
for (int i = 0; i < len; ++i) {
int p = a[i];
num[p]++;
if (num[p] % 2 == 0) {
if (str[i - 1] == '(') {
ans[p] += 1;
} else {
ans[p] += ans[a[i - 1]] * 2;
ans[a[i - 1]] = 0;
}
}
}
cout << ans[1] << endl;
return 0;
}
2. 最长配对 [哈希]
小b有一个01序列,她想找到一个最长的区间使得这个区间的01能两两配对,即0的个数和1的个数相等。求最长区间的长度。
输入
第一行一个正整数n,表示数组长度,其中0<n≤50000;
第二行n个0或1,以空格隔开。
输出
输出一个数,表示最长区间的长度
输入样例
3
0 1 0
输出样例
2
代码
#include<bits/stdc++.h>
using namespace std;
int solve() {
int n;
cin >> n;
unordered_map<int, int> mp; // prevSum, idx
mp.insert({0, -1});
int result = 0;
for (int i = 0, sum = 0, val; i < n; i++) {
cin >> val;
sum += (val == 1 ? 1 : -1);
mp.count(sum) ? result = max(result, i - mp[sum]) : mp[sum] = i;
}
return result;
};
int main() {
cout << solve();
return 0;
}
3. 梦中岛之路 [BFS]
小Y上课摸鱼睡着了,梦里,他在一座岛上,因为是梦,所以他有上帝视角,他看到另一座岛上有宝藏,但是两座岛不相连,于是乎他现在要把一些海水变成路使得两座岛连通。
由于小Y想省力,所以他会把尽量少的海水变成路,不过小Y作为一个计算机的学生,他已经做到看山不是山看水不是水,他现在眼中的岛是二位数组里的1,他眼中的水是二维数组里的0,那么刚才的问题就变成了,给你一个只包含01的二维数组,且岛是一个由1组成的四联通的块,0则是水,现在要求你把最少的0变成1,使得二维数组里有且仅有的两座岛相连。
输入
第一行一个整数N表示二维数组有N行N列
接下来N行,每行N个数字0或1,0代表水,1代表岛
1<=N<=100
输出
一个整数表示最少的需要把0变成1的数量
输入样例
3
010
000
001
输出样例
2
代码
#include<bits/stdc++.h>
using namespace std;
const vector<pair<int, int>> DIR{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int n, grid[101][101];
queue<pair<int, int>> q;
void prepare(int x, int y) {
if (x < 0 || y < 0 || x >= n || y >= n || grid[x][y] == 0 || grid[x][y] == 2) return;
q.emplace(pair<int, int>{x, y});
grid[x][y] = 2;
for (auto[dx, dy]:DIR) prepare(x + dx, y + dy);
}
int bfs() {
int result = 0;
while (!q.empty()) {
int size = q.size();
while (size--) {
auto[xc, yc]=q.front();
q.pop();
for (auto[dx, dy]:DIR) {
int xnext = xc + dx, ynext = yc + dy;
if (xnext < 0 || ynext < 0 || xnext >= n || ynext >= n || grid[xnext][ynext] == 2) continue; // 同一个岛屿
if (grid[xnext][ynext] == 1) return result; // 发现第二个岛屿
grid[xnext][ynext] = 2; // 归入第一个岛屿
q.push({xnext, ynext});
}
}
result++;
}
return 0;
}
int main() {
cin >> n;
int x0 = -1, y0 = -1; // 第一个岛屿位置
string s;
for (int i = 0; i < n; ++i) {
cin >> s;
for (int j = 0; j < n; ++j) {
grid[i][j] = (s[j] == '1');
if (x0 == -1 && grid[i][j] == 1) x0 = i, y0 = j; // 记录第一个岛屿位置
}
}
prepare(x0, y0); // 修改第一个岛屿标记为2, 并且将第一个岛屿所有位置加入深搜队列
cout << bfs();
return 0;
}
4. 小Biu的旅行 [BFS]
小Biu所在的城市有n个景点,有一些景点之间有单向联通的道路,现在小Biu在1号景点上,他想知道到达除了1号景点之外的每个景点分别最少需要经过多少条道路?
如图所示为样例数据,可以知道小Biu到达2号景点的最短路线为(1-2),到达3号景点的最短路线为(1-3),到达4号景点的最短路线为(1-2-4),到达5号景点的最短路线为(1-3-5),到达6号景点的最短路线为(1-3-5-6).所以答案分别为(1,1,2,2,3)
输入
第1行:两个正整数n,m,n表示景点的个数,m表示路径的条数。(1<=n<=1000,1<=m<=3000)
第2行-第m+1行:每行两个u,v,表示u到v有一条单向联通的道路,数据保证没有重边和自环。(1<=u,v<=n)
输出
输出n-1行,第i行表示从1号景点到达(i+1)号景点最少要经过几条道路,如果不能到达则输出-1。
输入样例
6 6
1 2
1 3
2 4
3 2
3 5
5 6
输出样例
1
1
2
2
3
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<unordered_set<int>> graph(n + 1);
for (int i = 0, from_, to_; i < m; ++i) {
cin >> from_ >> to_;
graph[from_].insert(to_);
}
vector<int> dist(n + 1);
vector<bool> visited(n + 1, false);
queue<int> q;
q.push(1);
int path = 0;
while (!q.empty()) {
int size = q.size();
while (size--) {
int node = q.front();
q.pop();
if (visited[node]) continue;
dist[node] = path;
visited[node] = true;
for_each(graph[node].begin(), graph[node].end(), [&](int x) {
if (!visited[x]) q.push(x);
});
}
path++;
}
for (int i = 2; i < dist.size(); ++i) cout << dist[i] << endl;
return 0;
}
5. 最小正子段和 [排序]
N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子段(a[i],a[i+1],…a[j]),使这个子段的和>0,并且这个和是所有和>0的子段中最小的。
例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和为1,是最小的。
输入
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数
输出
输出最小正子段和。
输入样例
8
4
-1
5
-2
-1
2
6
-2
输出样例
1
代码 1 [有序哈希]
时间复杂度有点高。。。
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
set<long long, greater<>> uset{0};
long long sum = 0, result = LONG_LONG_MAX, l_bound;
for (int i = 0, val; i < n; ++i) {
cin >> val;
sum += val;
auto it = uset.lower_bound(sum - 1);
if (it != uset.end()) {
result = min(result, sum - *it);
}
uset.insert(sum);
}
cout << result;
return 0;
}
代码 2 [有序哈希]
时间复杂度提高了一点,但是还是很慢。。。
#include<bits/stdc++.h>
using namespace std;
int main() {
map<long long, pair<int, int>> mp;
int n;
cin >> n;
long long sum = 0;
mp.insert({0ll, {-1, -1}});
for (int i = 0, val; i < n; ++i) {
cin >> val;
sum += val;
if (mp.count(sum)) {
mp[sum].second = i;
} else {
mp[sum] = {i, i};
}
}
long long result = INT_MAX;
auto i = mp.begin(), j = mp.begin();
for (++j; j != mp.end(); ++i, ++j) {
if (j->second.second > i->second.first) {
result = min(result, j->first - i->first);
}
}
cout << result;
return 0;
}
6. 小b和排序 [动态规划]
小b有两个长度都为n的序列A,B。
现在她需要选择一些i,然后交换A[i]和B[i],使得A和B都变成严格递增的序列。
你能帮小b求出最少交换次数吗?
其中1<=n<=1000, 0<=A[i],B[i]<=2000,输入保证有解。
输入
第一行输入一个正整数n,表示两个数组的长度;
第二行输入n个数,表示A[i],以空格隔开;
第三行输入n个数,表示B[i],以空格隔开;
输出
4
1 3 5 4
1 2 3 7
输入样例
1
输出样例
1
1
2
2
3
代码
#include<bits/stdc++.h>
using namespace std;
int A[1001], B[1001], dp[1001][2]; //dp[i][0]表示没有交换的次数,dp[i][1]表示交换的次数
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) cin >> A[i];
for (int i = 1; i <= n; ++i) cin >> B[i];
memset(dp, 127, sizeof dp); // 初始化为最大值
A[0] = B[0] = -1;
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; ++i) { //若两个序列中的第i个数都比第i-1个数大
if (A[i] > A[i - 1] && B[i] > B[i - 1]) {
dp[i][0] = min(dp[i][0], dp[i - 1][0]);
dp[i][1] = min(dp[i][1], dp[i - 1][1] + 1);
}
if (A[i] > B[i - 1] && B[i] > A[i - 1]) { //若序列A的第i个数大于序列B的第i-1个数且序列B的第i个数大于序列A的第i-1个数
dp[i][0] = min(dp[i][0], dp[i - 1][1]);
dp[i][1] = min(dp[i][1], dp[i - 1][0] + 1);
}
}
cout << min(dp[n][0], dp[n][1]);
return 0;
}
7. 顺子 [贪心]
小b有n张牌。 现在她想把牌分组,使得每组都是长度为W的顺子,即由连续W个数组成。
请问小b能做到吗?
已知1<=W<=n<=10000,任意牌的数字hand[i]满足0<=hand[i]<=10^9。
输入
第一行输入一个数n,表示手牌张数;
第二行输入n个非负整数,表示每张牌的数字,以空格隔开;
第三行输入一个数,表示每组大小W;
输出
可以分组,输出“true”;
不能分组,输出“false”。
输入样例
9
1 2 3 6 2 3 4 7 8
3
输出样例
true
代码
#include<bits/stdc++.h>
using namespace std;
bool solve() {
int n, w;
map<int, int> mp;
cin >> n;
for (int i = 0, val; i < n; ++i) {
cin >> val;
mp[val]++;
}
cin >> w;
if (w == 1) return true; // 剪枝
if (n % w != 0) return false; // 剪枝
auto it = mp.begin();
while (it != mp.end()) {
for (int i = it->first; i < it->first + w; ++i) {
if (!mp.count(i) || mp[i] == 0) return false;
mp[i]--;
}
while (it != mp.end() && it->second == 0) it++;
}
return true;
}
int main() {
cout << (solve() ? "true" : "false");
return 0;
}
8. 重排列得到2的幂 [哈希]
小b有一个数n,现在她想把n的每一位重排列,使得得到的结果为2的幂次。
请问小b能得到2的幂次吗?
注意重排列后不允许有前导0。
样例解释:46重排列成64,为2^6。
输入
输入一个数N,其中1≤N≤10^9
输出
满足条件,输出“true”;
不满足,则输出“false”。
输入样例
46
输出样例
true
代码
#include<bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
unordered_set<ull> uset;
ull encode(const string &s) {
ull result = 0;
for (auto ch:s) result += 1 << ((ch - '0') << 2);
return result;
}
int main() {
string s, p;
cin >> s;
ull target = encode(s);
for (int i = 0; i <= 30; ++i) { // 可选剪枝方法: 计算出31个对应值填入 set 或 vector
p = to_string(1 << i);
uset.insert(encode(p));
}
cout << (uset.count(target) ? "true" : "false");
return 0;
}
9. 重排列 [贪心]
有两个长度为n的序列A,B,你需要重排列A,使得满足A[i]>B[i]的i的数目尽量大。
只需输出这个最大个数即可。
输入
第一行输入一个正整数n;
第二、三行分别输出n个非负整数,表示A、B中的元素,以空格隔开;
其中1≤n≤10000,0≤A[i],B[i]≤10^9。
输出
输出一行一个数,表示最大个数
输入样例
4
2 7 11 15
1 10 4 11
输出样例
4
代码
#include<bits/stdc++.h>
using namespace std;
int n, a[10000], b[10000];
int main() {
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
sort(a, a + n);
sort(b, b + n);
int result = 0;
for (int i = 0, j = 0; i < n; ++i) {
if (a[i] > b[j]) {
result++;
j++;
}
}
cout << result;
return 0;
}
10. 和为K的倍数 [哈希/前缀和]
小b喜欢和为K的倍数的序列。
现在有一个长度为n的序列A,请问A有多少个非空连续子序列是小b喜欢的。
输入
第一行输入一个正整数n;
第二行输入n个整数,表示A[i],以空格隔开;
第三行输入一个正整数K;
其中1≤n≤30000,对于任意A[i]有-10000≤A[i]≤10000,2≤K≤10000
输出
输出一个数,表示子序列的数目
输入样例
6
4 5 0 -2 -3 1
5
输出样例
7
代码
#include<bits/stdc++.h>
using namespace std;
int nums[30000];
int main() {
int n, k;
cin >> n;
for (int i = 0; i < n; ++i) cin >> nums[i];
cin >> k;
vector<int> mp(k, 0);
mp[0] = 1;
unsigned long long result = 0;
int add_ = (10000 + k) / k * k;
for (int i = 0, sum = 0; i < n; ++i) {
sum = (sum + nums[i] + add_) % k;
result += mp[sum];
mp[sum]++;
}
cout << result;
return 0;
}
11. 低买高卖 [优先级队列]
考虑股票市场上的某一只股票,一共有 n 天。
对于第 i 天,B 君知道股票的价格是每单位 a[i] 元
在每一天,B 君可以选择买入一个单位的股票,或卖出一个单位的股票,或者什么都不做。
刚开始 B 君有无穷多的钱,但是没有任何股票。问 n 天之后 B 君最多可以赚多少钱。
输入
第一行一个整数n表示天数。(1 <= n <= 200000,1 <= a[i] <= 10000)
接下来一行n个整数,表示每天的价钱。
输出
一行一个整数表示最多可以赚的钱数。
输入样例
9
10 5 4 7 9 12 6 2 10
输出样例
20
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, val, sum = 0;
cin >> n;
priority_queue<int, vector<int>, greater<>> pue;
while (n--) {
cin >> val;
if (!pue.empty() && val > pue.top()) {
sum += val - pue.top();
pue.pop();
pue.push(val);
pue.push(val);
} else {
pue.push(val);
}
}
cout << sum;
return 0;
}
12. 小b删列 [**]
小b有一个n*m的矩阵A,矩阵的每个元素为一个字符,现在她希望删除其中的一些列,使得剩下的列在每一行形成的字符串的字典序不降。
即对于第i行,将剩下的列上的字符顺序拼接,形成一个字符串,字符串记作a[i]。要求a[i]<=ai+1。
请问小b最少要删多少列。
如A = {“abcdef”, “uvwxyz”},删除的列为第1,3,4列,删除后 A 为 {“bef”, “vyz”},且 “bef” <= “vyz”
样例解释:
删掉第一列,剩下的是"a" “b” “c”,“a” <= “b” <= “c”,满足条件。
输入
第一行输入一个正整数n,表示矩阵A的行数;
之后n行每行输入一个字符串,其长度相等;
1≤n,m≤100。
输出
输出一个非负整数,表示删掉的列数
输入样例
3
ca
bb
ac
输出样例
1
代码
#include<bits/stdc++.h>
using namespace std;
string G[101];
bool hascut[101];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> G[i];
int len = G[0].length(), res = 0;
for (int i = 1; i < n; ++i) { // 遍历行
bool reset = false; // 删列重新遍历
for (int j = 0; j < len; ++j) {
if (hascut[j] || G[i][j] == G[i - 1][j]) continue; // 该列已删除or列相等
if (G[i][j] > G[i - 1][j]) break; // 字典序严格大于, 停止判断
hascut[j] = true; // 删除 j 列
res++;
reset = true; // 重新遍历
}
if (reset) i = 0;
}
cout << res << endl;
return 0;
}
题单来源:QQ群200162761
。