A.Online Shopping(计算)
题意:
需要购买 N N N种物品,第 i i i种物品的价格为 P i P_i Pi,且第 i i i件物品需买 Q i Q_i Qi件。
商店满 S S S元包邮,不满则需支付 K K K元邮费,问需支付多少钱。
分析:
按照要求计算,如果商品总价没到包邮门槛,那么就加上邮费。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 3e5 + 5;
void solve() {
int n, m, k;
cin >> n >> m >> k;
LL ans = 0;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
ans += a * b;
}
if (ans < m) ans += k;
cout << ans << endl;
}
int main() {
solve();
return 0;
}
B.Glass and Mug(模拟)
题意:
有两个杯子,一个玻璃杯,一个马克杯,每个杯子都有一个容量,且保证马克杯的容量比玻璃杯大。
你需要按以下要求进行 K K K次操作:
-
如果玻璃杯装满水,将玻璃杯中水倒完
-
如果玻璃没有装满水,且马克杯是空的,往马克杯里加满水
-
否则,将马克杯里的水倒入玻璃杯中,直到马克杯空或玻璃杯满
分析:
按题目要求模拟即可。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 3e5 + 5;
void solve() {
int k, g, m;
cin >> k >> g >> m;
int glass = 0, mug = 0;
while (k--) {
if (glass == g) {
glass = 0;
} else if (mug == 0) {
mug = m;
} else {
int add = min(mug, g - glass);
glass += add;
mug -= add;
}
}
cout << glass << ' ' << mug << endl;
}
int main() {
solve();
return 0;
}
C.T-shirts(模拟)
题意:
给出一个长度为 N N N的字符串,字符串第 i i i个字符含义如下:
-
0:在家休息(不需要穿T恤,且会把所有穿过的T恤洗掉,下一天所有T恤均可穿)
-
1:出门吃饭(可以选择穿纯色T恤或带
logo
的T恤) -
2:参加比赛(必须选择带
logo
的T恤)
开始时拥有
M
M
M件纯色的T恤,问至少拥有多少件带logo
的T恤才能保证这
N
N
N天都有衣服穿?
分析:
按要求进行模拟,如果休息就将穿过的T恤清空,如果出门吃饭,有纯色T恤就选纯色,没有就选带logo
的,参加比赛也选择带logo
的,记录过程中,最多穿过的带logo
的T恤数量即可。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 3e5 + 5;
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
int plain = 0, logo = 0, ans = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '0') {
plain = logo = 0;
} else if (s[i] == '1') {
if (plain < m) {
plain++;
} else {
logo++;
}
} else {
logo++;
}
ans = max(ans, logo);
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}
D.Swapping Puzzle(枚举全排列)
题意:
给出两个 H × W H \times W H×W的网格 A A A和 B B B,每次可以选择一个数字 i i i进行以下两种操作之一:
-
交换第 i i i行和第 i + 1 i + 1 i+1行所有元素
-
交换第 i i i列和第 i + 1 i + 1 i+1列所有元素
问:输出将网格A
变为网格B
的最少操作次数,如果无法完成,输出出-1
。
分析:
行列的交换是互相不会产生影响的,因此可以使用两层循环使用全排列函数next_permutation
对行列的最终排列进行枚举,然后检查是否满足题意,记录最小的交换次数即可。
Tips:不难发现,行列的交换次数为序列中逆序对数量,如序列{1, 3, 2, 5, 4},里面存在两对逆序对 ( 3 , 2 ) , ( 5 , 4 ) (3, 2), (5, 4) (3,2),(5,4),因此该序列是经过两次交换得到的。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 5;
int row[10] = {0, 1, 2, 3, 4, 5}, colum[10] = {0, 1, 2, 3, 4, 5};
int n, m, a[10][10], b[10][10];
int getCost() {
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (row[i] > row[j]) {
res++;
}
}
}
for (int i = 1; i <= m; i++) {
for (int j = i + 1; j <= m; j++) {
if (colum[i] > colum[j]) {
res++;
}
}
}
return res;
}
bool check() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[row[i]][colum[j]] != b[i][j]) {
return false;
}
}
}
return true;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> b[i][j];
}
}
do {
do {
if (check()) {
cout << getCost() << endl;
return;
}
} while (next_permutation(colum + 1, colum + m + 1));
} while (next_permutation(row + 1, row + n + 1));
cout << -1 << endl;
}
int main() {
solve();
return 0;
}
E.Lucky bag(二进制枚举,DP)
题意:
有 N N N件物品, M M M个幸运袋,每个物品均需被装入到一个袋子中,然后要求找到所有装入方案中方差最小中一种,即使 V V V最小:
- V = 1 D ∑ i = 1 D ( x i − x ‾ ) 2 V = \frac{1}{D}\sum\limits_{i = 1}^{D}(x_i - \overline{x})^{2} V=D1i=1∑D(xi−x)2
其中 x i x_i xi为第 i i i个袋子中的物品价值总和, x ‾ \overline{x} x为袋子的平均价值。
分析:
可以先预处理一个幸运袋中所有的物品装入方案,使用二进制枚举进行。
然后使用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 0 ∼ i 0 \sim i 0∼i的袋子中放入了方案 j j j包含的物品的最小方差。
每一次计算 d p [ i ] [ j ] dp[i][j] dp[i][j]时,通过枚举所有方案 j j j的子集,并将当前袋子装入剩下部分的子集,使得两个子集刚好可以拼成方案 j j j,记录最小的方差。
Tips:枚举子集如果每次均通过减一的方式,时间复杂度就会变得很高,而每次减一后,再通过与当前枚举的集合 j j j进行与运算,既保证了当前方案为集合 j j j的子集,且只从上一个方案减一,即、也取到了下一个最大的方案。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 5e4;
int n, m;
double w[20];
double s[N];
double dp[20][N];
void solve() {
double avg = s[(1 << n) - 1] / m;
for (int i = 0; i < m; i++) {
for (int j = (1 << n) - 1; j >= 0; j--) {
if (i == 0) {
dp[i][j] = (s[j] - avg) * (s[j] - avg);//第一个幸运袋,直接放入
} else {
dp[i][j] = dp[i - 1][j] + avg * avg;//先假设当前袋子不放东西(需加上(avg - 0)^2)
for (int k = j; k; k = (k - 1) & j) {//枚举子集
dp[i][j] = min(dp[i][j], dp[i - 1][j ^ k] + (s[k] - avg) * (s[k] - avg));//dp[0][k]可换为(s[k] - avg)^2
}
}
}
}
cout << fixed << setprecision(15) << dp[m - 1][(1 << n) - 1] / m << endl;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> w[i];
for (int i = (1 << n) - 1; i >= 0; i--) {
for (int j = 0; j < n; j++) {
if ((i >> j) & 1) {
s[i] += w[j];
}
}
}
solve();
return 0;
}
F.Random Update Query(线段树,期望)
题意:
给出一个长度为 N N N的数组 A = A 1 , A 2 , . . . , A N A = A_1, A_2, ..., A_N A=A1,A2,...,AN。
对这个数组进行以下操作 M M M次:
-
随机的选择区间 L i ∼ R i L_i \sim R_i Li∼Ri上的一个点(概率均等)
-
将这个点修改为 X i X_i Xi
问结果 M M M次操作后,每个位置上的数字的期望是多少。
分析:
随机的选择区间上的数字服从于均匀分布,则每个数字被修改的概率为 1 len \frac{1}{\text{len}} len1,其中 l e n len len为区间内的数字个数。那么对于每个数字来说实际上服从于 01 01 01分布,即 A i A_i Ai的期望为:
- E ( A i ) = A i × l e n − 1 l e n + X i × 1 l e n E(A_i) = A_i \times \frac{len - 1}{len} + X_i \times \frac{1}{len} E(Ai)=Ai×lenlen−1+Xi×len1
对于每次操作,可以先对区间乘上 l e n − 1 l e n \frac{len - 1}{len} lenlen−1,再加上 X i × 1 l e n X_i \times \frac{1}{len} Xi×len1,区间操作使用线段树辅助实现。
注意:
-
除法转为乘法逆元
-
双标记先推乘法,再推加法
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 998244353;
const int N = 2e5 + 5e2;
LL qpow(LL a, LL b) {
LL res = 1;
while (b) {
if (b & 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
LL n, m, E[N << 2], add[N << 2], mul[N << 2];
void pushup(int x) {
E[x] = (E[x << 1] + E[x << 1 | 1]) % MOD;
}
void build (int l, int r, int x) {
if (l == r) {
cin >> E[x];
return;
}
mul[x] = 1;
int mid = (l + r) >> 1;
build(l, mid, x << 1);
build (mid + 1, r, x << 1 | 1);
pushup(x);
}
void pushdown(int l, int r, int x) {
add[x << 1] = (add[x << 1] * mul[x] % MOD + add[x]) % MOD;
add[x << 1 | 1] = (add[x << 1 | 1] * mul[x] % MOD + add[x]) % MOD;
mul[x << 1] = (mul[x << 1] * mul[x]) % MOD;
mul[x << 1 | 1] = (mul[x << 1 | 1] * mul[x]) % MOD;
LL mid = (l + r) >> 1;
E[x << 1] = (E[x << 1] * mul[x] % MOD + add[x] * (mid - l + 1) % MOD) % MOD;
E[x << 1 | 1] = (E[x << 1 | 1] * mul[x] % MOD + add[x] * (r - mid) % MOD) % MOD;
mul[x] = 1;
add[x] = 0;
}
void update(int l, int r, int x, int ul, int ur, int op, LL val) {
if (l >= ul && r <= ur) {
if (op) {//乘法
mul[x] = (mul[x] * val) % MOD;
add[x] = (add[x] * val) % MOD;
E[x] = (E[x] * val) % MOD;
} else {//加法
add[x] = (add[x] + val) % MOD;
E[x] = (E[x] + val * (r - l + 1) % MOD) % MOD;
}
return;
}
pushdown(l, r, x);
int mid = (l + r) >> 1;
if (ul <= mid) update(l, mid, x << 1, ul, ur, op, val);
if (ur > mid) update(mid + 1, r, x << 1 | 1, ul, ur, op, val);
}
void print(int l, int r, int x) {
if (l == r) { if (l != 1) cout << ' ';
cout << E[x];
return;
}
pushdown(l, r, x);
int mid = (l + r) >> 1;
print(l, mid, x << 1);
print(mid + 1, r, x << 1 | 1);
}
int main () {
cin >> n >> m;
build(1, n, 1);
while (m--) {
LL l, r, x;
cin >> l >> r >> x;
LL num = (r - l) * qpow(r - l + 1, MOD - 2) % MOD;
update(1, n, 1, l, r, 1, num);
num = x * qpow(r - l + 1, MOD - 2) % MOD;
update(1, n, 1, l, r, 0, num);
}
print(1, n, 1);
return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。