A. 简单判断
题意:给定一个字符串,判断是以 ererer 结尾还是以 ististist 结尾
判断最后一个字母是 rrr 还是 ttt 即可
#include<bits/stdc++.h>
using namespace std;
string s;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> s;
if (s.back() == 'r') cout << "er\n";
else cout << "ist\n";
return 0;
}
B. 暴力枚举
题意:给定一个 H×WH\times WH×W 的矩阵,判断对于所有的 1≤i1≤i2≤H,1≤j1≤j2≤W1\leq i_1\leq i_2\leq H, 1\leq j_1\leq j_2\leq W1≤i1≤i2≤H,1≤j1≤j2≤W,是否满足 Ai1,j1+Ai2,j2≤Ai2,j1+Ai1,j2A_{i_1, j_1} + A_{i_2, j_2}\leq A_{i_2, j_1} + A_{i_1, j_2}Ai1,j1+Ai2,j2≤Ai2,j1+Ai1,j2
暴力枚举所有的 i1,i2,j1,j2i_1, i_2, j_1, j_2i1,i2,j1,j2 即可,时间复杂度:O(H2W2)O(H^2W^2)O(H2W2)
#include<bits/stdc++.h>
using namespace std;
int n, m;
const int N = 55;
int a[N][N];
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i1 = 1; i1 <= n; i1++) {
for (int i2 = i1 + 1; i2 <= n; i2++) {
for (int j1 = 1; j1 <= m; j1++) {
for (int j2 = j1 + 1; j2 <= m; j2++) {
if (a[i1][j1] + a[i2][j2] > a[i2][j1] + a[i1][j2]) {
cout << "No\n";
return 0;
}
}
}
}
}
cout << "Yes\n";
return 0;
}
C. 暴力枚举
题意:给定 NNN 个不重复的点,判断能组成多少个三角形
因为 N≤300N\leq 300N≤300 这一条件,所以我们可以直接暴力 O(N3)O(N^3)O(N3) 枚举所有的点组合,并判断每个点组合是否可行即可
判断 (xi,yi),(xj,yj),(xk,yk)(x_i, y_i), (x_j, y_j), (x_k, y_k)(xi,yi),(xj,yj),(xk,yk) 是否可行,即判断其是否共线
- 令 d1=(xj−xi,yj−yi),d2=(xk−xi,yk−yi)d_1 = (x_j - x_i, y_j - y_i), d_2 = (x_k - x_i, y_k - y_i)d1=(xj−xi,yj−yi),d2=(xk−xi,yk−yi)
- 如果 d1,d2d_1, d_2d1,d2 共线,那么 d1×d2=(xj−xi)(yk−yi)−(yj−yi)(xk−xi)=0d_1 \times d_2 = (x_j - x_i)(y_k - y_i) - (y_j - y_i)(x_k - x_i) = 0d1×d2=(xj−xi)(yk−yi)−(yj−yi)(xk−xi)=0,注意这里是向量叉积,我们只需要判断上式是否成立即可
- 反之不共线
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e2 + 5;
int n, x[N], y[N];
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
ll cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
for (int k = j + 1; k <= n; k++) {
int dx1 = x[j] - x[i], dy1 = y[j] - y[i];
int dx2 = x[k] - x[i], dy2 = y[k] - y[i];
cnt += 1ll * dx1 * dy2 - 1ll * dx2 * dy1 != 0;
}
}
}
cout << cnt << '\n';
return 0;
}
D. 暴力模拟
题意:8-puzzle 问题的变种,求将 111 到 888 编号的棋子放到 111 到 999 编号的节点上的最小步数,同一时间每个节点最多只能放 111 个棋子,节点之间存在转移边
总状态数为 9!9!9!,转移数最大为 999,所以暴力模拟的复杂度为 O(9×9!)O(9\times 9!)O(9×9!),需要记录当前状态是否被走过
因为需要求的是最小操作次数,所以需要 bfsbfsbfs 而不能用 dfsdfsdfs,具体细节见代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
int m, vis[N];
vector<int> nxt[N];
map<vector<int>, int> d;
vector<int> tar = {0, 1, 2, 3, 4, 5, 6, 7, 8};
void bfs(vector<int> p) {
queue<vector<int>> quu;
quu.push(p);
d[p] = 0;
while (!quu.empty()) {
vector<int> now = quu.front(); quu.pop();
if (now == tar) {
cout << d[now] << '\n';
return;
}
fill(vis, vis + 10, 0);
for (int i = 1; i <= 8; i++) {
vis[now[i]] = 1;
}
for (int i = 1; i <= 8; i++) {
int pos = now[i], step = d[now];
for (auto it: nxt[pos]) {
if (vis[it]) continue;
vis[pos] = 0;
vis[it] = 1;
now[i] = it;
if (!d.count(now)) {
d[now] = step + 1;
quu.push(now);
}
now[i] = pos;
vis[pos] = 1;
vis[it] = 0;
}
}
}
cout << -1 << '\n';
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> m;
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
nxt[u].push_back(v);
nxt[v].push_back(u);
}
vector<int> p(9, 0);
for (int i = 1; i <= 8; i++) {
cin >> p[i];
}
bfs(p);
return 0;
}
E. 动态规划
题意:在一个方格上走,每次只能走同一行或同一列,且到达的格点上的值必须严格大于当前值,问最多走多少步
我们可以按行和列进行 dpdpdp
令 row[i]row[i]row[i] 表示当前第 iii 行的数向后最多能走多少步,col[i]col[i]col[i] 表示当前第 iii 列的数向后最多能走多少步
可以将数从大向小加入方格中,维护 row[i]row[i]row[i] 和 col[i]col[i]col[i] 的变化
对于即将加入个点的数 (r,c,a)(r, c, a)(r,c,a),他的贡献是 row[r]←row[r]+1,col[c]←col[c]+1row[r] \leftarrow row[r] + 1, col[c]\leftarrow col[c] + 1row[r]←row[r]+1,col[c]←col[c]+1
需要注意的是,移动到的点必须是值严格大于他的,所以不能一个数一个数去加,而应该对于具有相同值的数一起加,再统一更新
时间复杂度:O(NlogN)O(N\log N)O(NlogN)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int h, w, n, row[N], col[N], r[N], c[N], a[N], ans[N];
map<int, vector<int>> mp;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> h >> w >> n;
for (int i = 1; i <= n; i++) {
cin >> r[i] >> c[i] >> a[i];
mp[-a[i]].push_back(i);
}
for (auto it: mp) {
vector<int>& now = it.second;
for (auto id: now) {
ans[id] = max(row[r[id]], col[c[id]]);
}
for (auto id: now) {
row[r[id]] = max(row[r[id]], ans[id] + 1);
col[c[id]] = max(col[c[id]], ans[id] + 1);
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << '\n';
}
return 0;
}
F. 数学公式推导
题意:给定一个数字串,向其中随意放入加号组成表达式,问所有情况的表达式值的和是多少
很显然这是一个推公式的题
假如当前是第 iii 位,当前数是第 jjj 位,这样的情况数有多少种呢
- i+j,i+j+1,...,Ni + j, i + j + 1, ... , Ni+j,i+j+1,...,N 随意分配加号,情况数为 2N−i−j2^{N - i - j}2N−i−j 种,需要注意的是 i+j−1=Ni + j - 1 = Ni+j−1=N 时情况数为 111,这种情况分开讨论
- 1,2,...,i−11, 2, ..., i - 11,2,...,i−1 随意分配加号,i−1i - 1i−1 后面可分配可不分配,一共 2i−12^{i - 1}2i−1 种情况
那么,答案表达式为:
KaTeX parse error: No such environment: align at position 8:
\begin{̲a̲l̲i̲g̲n̲}̲
ans &= \sum\li…
对于 ∑i=1Nci×(2i−1×10N−i)\sum\limits_{i = 1}^{N}c_i \times(2^{i - 1}\times 10^{N - i})i=1∑Nci×(2i−1×10N−i) 可以 O(N)O(N)O(N) 求
对于 ∑i=1Nci×∑j=1N−i2N−j−1×10j−1\sum\limits_{i = 1}^{N}c_i\times \sum\limits_{j = 1}^{N - i}2^{N - j - 1}\times 10^{j - 1}i=1∑Nci×j=1∑N−i2N−j−1×10j−1,其实不难发现 ∑j=1N−i2N−j−1×10j−1\sum\limits_{j = 1}^{N - i}2^{N - j - 1}\times 10^{j - 1}j=1∑N−i2N−j−1×10j−1 是一个表达式内与 iii 无关的量,我们只需要预处理出 ∑j=1q2N−j−1×10j−1\sum\limits_{j = 1}^{q}2^{N - j - 1}\times 10^{j - 1}j=1∑q2N−j−1×10j−1 的表即可,后续就可以直接 O(1)O(1)O(1) 查了,所以复杂度也是 O(N)O(N)O(N)
总时间复杂度:O(N)O(N)O(N)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, mod = 998244353;
char s[N];
int n, ans, pw2[N], pw10[N], v[N];
int main(void) {
scanf("%s", s + 1);
n = strlen(s + 1);
pw2[0] = pw10[0] = 1;
for (int i = 1; i <= n; i++) {
pw2[i] = 1ll * pw2[i - 1] * 2 % mod;
pw10[i] = 1ll * pw10[i - 1] * 10 % mod;
}
for (int i = 1; i < n; i++) {
v[i] += (v[i - 1] + 1ll * pw2[n - i - 1] * pw10[i - 1]) % mod;
}
for (int i = 1; i <= n; i++) {
int c = s[i] - '0';
int tmp = 0;
for (int j = 1; j <= n - i; j++) {
tmp += 1ll * pw2[n - j - 1] % mod * pw10[j - 1] % mod;
tmp %= mod;
}
ans += 1ll * c * v[n - i] % mod; ans %= mod;
ans += 1ll * pw2[2, i - 1] * c % mod * pw10[10, n - i] % mod; ans %= mod;
}
cout << ans << '\n';
return 0;
}
G. 概率论
题意:给定起点 SSS 与终点 TTT,可以做两个操作
- S←S+1S\leftarrow S+1S←S+1,代价为 AAA
- 将 SSS 随机变为 [1,N][1, N][1,N] 的随机一个整数,代价为 BBB
问在最优策略下,从 SSS 走到 TTT 的期望代价最小是多少
关键在于选什么样的策略
有两种可能的情况:
- 先随机走,如果随机到一个比较好的值,就一直加到 TTT
- 如果满足 S≤TS\leq TS≤T,的话可以直接加到 TTT
不难发现,这两种做法实际上是完全独立的,也就是说不可能出现先加再随机的情况(这肯定不好),在一开始就要选定是用两种策略里的哪一种
事实上,我们只需要计算两种策略的最小值就是答案
对于第二种情况很简单,就是 (T−S)×A(T - S)\times A(T−S)×A
对于第一种情况,一定是有一个阈值 x,(x≤T)x,(x\leq T)x,(x≤T),如果随机到的数满足 [x,T][x, T][x,T],就一直加到 TTT,反之继续随机
随机到 [x,T][x, T][x,T] 的期望步数为 NT−x+1\frac{N}{T - x + 1}T−x+1N,期望值为 NT−x+1B\frac{N}{T - x + 1}BT−x+1NB
对已随机到 [x,T][x, T][x,T] 的所有情况,其到达 TTT 的数学期望为 0+1+...+T−xT−x+1=T−x2\frac{0 + 1 + ... + T - x}{T - x + 1} = \frac{T - x}{2}T−x+10+1+...+T−x=2T−x,期望值为 T−x2A\frac{T - x}{2}A2T−xA
总期望为 NT−x+1B+T−x2A\frac{N}{T -x + 1}B + \frac{T - x}{2}AT−x+1NB+2T−xA,我们需要选定一个合适的 xxx,来使这个表达式最小
不难发现上述表达是是一个钩型函数 x+axx + \frac{a}{x}x+xa 的形式,所以我们可以算出其解析解,或者三分求最小值
将表达式的最小值与情况 222 的值取最小值即为答案
时间复杂度:O(1)O(1)O(1)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
int n, s, t, a, b;
double f(int x) {
return 1.0 * a / 2 * (t - x) + 1.0 * n * b / (t - x + 1);
}
double bin3(void) {
int l = 1, r = t;
double ret = min(f(l), f(r));
while (l <= r) {
int m = (l + r) / 2, mm = (m + r) / 2;
double fm = f(m), fmm = f(mm);
if (fm >= fmm) l = m + 1;
else r = mm - 1;
ret = min(ret, min(fm, fmm));
}
return ret;
}
signed main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> s >> t >> a >> b;
cout.precision(50);
double ans = bin3();
if (s <= t) ans = min(ans, 1.0 * (t - s) * a);
cout << ans << endl;
return 0;
}
本文介绍了多种字符串处理和算法题目,包括简单的字符串判断、矩阵遍历验证、三角形计数、8-puzzle问题的最短步数、动态规划解决数列问题以及概率论中的最优策略计算。每个题目都提供了相应的解决方案,涉及暴力枚举、动态规划、数学公式推导等方法。
1419

被折叠的 条评论
为什么被折叠?



