Codeforces Round 938 (Div. 3)
也是终于补完了题目,前面还好,最后一题状态压缩dp优点搞不懂,继续加油吧各位
A. Yogurt Sale
链接:Problem - A - Codeforces
题意:
由于促销活动原价一份 a a a布尔,现在两份 b b b布尔,问现在想买 n n n份,最少要花多少钱 ? ? ?
思路:
只需要比较 2 ∗ a 2 * a 2∗a与 b b b的大小,然后看买的份数是奇数还是偶数
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve() {
int n, a, b;
cin >> n >> a >> b;
int ans = 0;
if (2 * a <= b)//判断那个更小
ans = n * a;//单价更小就直接一份一份买
else//否则两份两份买,看是否有多的一份加上a布尔即可
ans = b * (n / 2) + a * (n % 2);
cout << ans << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
B. Progressive Square
链接:Problem - B - Codeforces
题意:
有一个 n ∗ n n*n n∗n的二维矩阵,知道三个值 a 1 , 1 、 c 、 d a_{1,1}、c、d a1,1、c、d根据以下规定创建的:
a i + 1 , j = a i , j + c a_{i+1,j} = a_{i,j} + c ai+1,j=ai,j+c
a i , j + 1 = a i , j + d a_{i,j+1} = a_{i,j} + d ai,j+1=ai,j+d
现在给你三个数 n 、 c n、c n、c和 d d d,以及一个长度为 n ∗ n n*n n∗n的一位数组,判断这个数组是否可以组成根据以上规则组成的二维矩阵。
思路:
直接根据给的值,去找出组成这个二维矩阵所需要的所有值,然后判断这两个数组是否相等,如果相等输出 y e s yes yes,否则输出 n o no no
代码:
#include <iostream>
#include<vector>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<deque>
#include<unordered_set>
#include<unordered_map>
#define int long long
using namespace std;
void solve() {
int n, c, d;
cin >> n >> c >> d;
vector<int> a, v;//a数组用来存储给定的值,v用来存储求出来所需要的值
for (int i = 1; i <= n * n; i++) {
int x;
cin >> x;
a.push_back(x);
}
sort(a.begin(), a.end());//排序
v.push_back(a[0]);//因为c、d都是正数,所以从最小的值开始
for (int i = 1; i < n * n; i++) {
if (i % n == 0)//如果换行了,则接下来的值用上一行的值加上c
v.push_back(v[i - n] + c);
else//如果没换行,则用上一个值加上d
v.push_back(v[i - 1] + d);
}
sort(v.begin(), v.end());//排序
if (a == v)//判断连个数组是否相等即可
cout << "YES" << endl;
else
cout << "NO" << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
C. Inhabitant of the Deep Sea
链接:Problem - C - Codeforces
题意:
给定长度为 n n n的数组 a a a,以及一个数 k k k,可以按顺序进行以下操作:先最左边的数减一,然后最右边的数减一。如果最左边的数减到了 0 0 0,那么最左边的数就往右移一位;如果最右边的数减到了 0 0 0,那么最右边的数往左移一位,问在 k k k次操作里最多可以让几个数变为 0 0 0。
思路:
由于他是左边一次右边一次,那么可以将 k k k分为两份,依次判断左右两边可以让多少个数减到 0 0 0
代码:
#include <iostream>
#include<vector>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<deque>
#include<unordered_set>
#include<unordered_map>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];
void solve() {
int n, k;
cin >> n >> k;
int sum = 0;//用来存放所有数的和
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
if (k >= sum) {//如果k大于所有数的和,则直接输出数组长度
cout << n << endl;
return;
}
//l表示左边分配的值,r表示右边分配的值
int l = (k + 1) / 2, r = k / 2;
int ans = 0;//用来存放可以变为零的总数
for (int i = 1; i <= n; i++) {
if (a[i] <= l) {
ans++;
l -= a[i];
}
else
break;
}
for (int i = n; i > 0; i--) {
if (a[i] <= r) {
ans++;
r -= a[i];
}
else
break;
}
cout << ans << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
D. Inaccurate Subsequence Search
链接:Problem - D - Codeforces
题意:
给定长度为 n n n的数组 a a a和长度为 m m m的数组 b b b,在数组 a a a中截取长度为 m m m的子数组,如果子数组中的元素与数组 b b b中的元素至少有 k k k个相等,则表示这是一个好数组,问有多少个好数组
思路:
简单模拟、滑动窗口
代码:
#include <iostream>
#include<vector>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<deque>
#include<unordered_set>
#include<unordered_map>
#define int long long
using namespace std;
const int N = 1e6+ 10;
int a[N], b[N],c[N], d[N];
void solve() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) {
cin >> b[i];
c[b[i]]++;
}
int ans = 0, tep = 0;
for (int i = 1; i <= n; i++) {
d[a[i]]++;
if (d[a[i]] <= c[a[i]])
tep++;
if (i > m) {
d[a[i - m]]--;
if (d[a[i - m]] < c[a[i - m]])
tep--;
}
if (i >= m && tep >= k) ans++;
}
for (int i = 1; i <= n; i++) d[a[i]] = 0;
for (int i = 1; i <= m; i++) c[b[i]] = 0;
cout << ans << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
E. Long Inversions
链接:Problem - E - Codeforces
题意:
给定一个长度为 n n n的二进制字符串 s s s,找到一个最大的 k k k能够使得字符串 s s s中连续的 k k k个字符反转( 1 1 1变成 0 0 0, 0 0 0变成 1 1 1)经历任意次数后让字符串 s s s中的所有值变为 1 1 1。
思路:
暴力 + 差分
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve() {
int n;
string s;
cin >> n >> s;
s = '?' + s;//让字符串从一开始计算,便于之后的运算
for (int k = n; k > 0; k--) {//从大开始判断,因为是找最大值
vector<int> v(n + 2);//定义差分数组
int ok = 1;//用来判断是否满足条件,如果满足则输出
for (int i = 1; i <= n; i++) {//从头开始遍历
v[i] ^= v[i - 1];
int x = s[i] - '0';
if ((x ^ v[i]) != 1) {//如果当前位置上不是1的话
if (i + k - 1 > n) {//判断是不是超出了字符串的长度
ok = 0;
break;
}
//差分
v[i] ^= 1;
v[i + k] ^= 1;
}
}
if (ok) {//如果满足条件,则直接输出这个k
cout << k << endl;
return;
}
}
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
F. Unfair Game
链接:Problem - F - Codeforces
题意:
给定一个长度为 4 4 4的数组 v v v,表示数字 1 、 2 、 3 、 4 1、2、3、4 1、2、3、4的出现的个数,如果所有数的异或等于 0 0 0,表示鲍勃赢了 ,每次删除一个数,求鲍勃赢得最大次数
思路:
- 如果所有都为偶数则结果为:
v [ 1 ] / 2 + v [ 2 ] / 2 + v [ 3 ] / 2 + v [ 4 ] / 2 v[1] / 2 + v[2] / 2 + v[3] / 2 + v[4] / 2 v[1]/2+v[2]/2+v[3]/2+v[4]/2 - 特殊情况:如果 1 1 1~ 3 3 3的个数都是奇数,结果加一
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve() {
vector<int> v(5);
for (int i = 1; i <= 4; i++) cin >> v[i];
int ans = v[1] / 2 + v[2] / 2 + v[3] / 2 + v[4] / 2;
if (v[1] % 2 && v[2] % 2 && v[3] % 2) ans++;
cout << ans << endl;
}
signed main()
{
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
G. GCD on a grid
链接:Problem - G - Codeforces
题意:
给定一个 n ∗ m n*m n∗m的二维数组 a a a,问从左上角到右下角(只能往下走或者往右走)的最大公约数是多少?
思路:
暴力 + 动态规划
由于是找最大公约数,由于 a [ 1 ] [ 1 ] a[1][1] a[1][1]和 a [ n ] [ m ] a[n][m] a[n][m]一定存在,所以直接暴力枚举这两个数的最大公约数的因子,如果一条路上的所有数都是这个最大公约数的因子的倍数的话,则表示这个数成立,找到最大的这个数即可
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 110;
int a[N][N] , f[N][N];//a用来存储二维数组,f用来表示这个数是否是最大公约数的倍数
//求最大公约数(欧几里得算法)
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
void solve() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
f[i][j] = 0;
}
}
//相当于写了一个函数用来判断当前公约数是否可以从左上角走到右下角
auto check = [&](int k) {
f[1][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] % k == 0)
f[i][j] = (f[i - 1][j] | f[i][j - 1]);
else
f[i][j] = 0;
}
}
return f[n][m];
};
//因为一定经过左上角和右下角,所以结果一定是左上角与右下角的最大公约数的因子
int t = gcd(a[1][1], a[n][m]);
int ans = 0;//记录结果
for (int i = 1; i * i <= t; i++) {
if (t % i == 0) {//一一遍历因子
if (check(i)) ans = max(ans, i);
if (check(t / i)) ans = max(ans, t / i);
}
}
cout << ans << endl;
}
signed main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
H. The Most Reckless Defense
链接:Problem - H - Codeforces
这题不太会,搞不懂状态压缩dp是怎么搞出来的,只能把代码复制一遍了(笑哭)
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 55;
char s[N][N];
int f[2][1 << 12];//滚动的压缩dp
void solve() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) cin >> (s[i] + 1);
int u = 0;
for (int i = 0; i < 1 << 12; i++) f[u][i] = 0;
while (k--) {
int x, y, p;
cin >> x >> y >> p;
int cnt[13] = { 0 };
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i][j] == '#') {
//ceil向上取整,hypot求斜边
int r = ceil(hypot(i - x, j - y));
if (r <= 12) cnt[r]++;
}
}
}
for (int i = 1; i <= 12; i++) cnt[i] += cnt[i - 1];
u ^= 1;//每次异或相当于在0、1之间跳动,滚动的dp数组
//后面就看不太懂了
for (int i = 0; i < 1 << 12; i++) {
f[u][i] = f[u ^ 1][i];
int w = 1;
for (int j = 1; j <= 12; j++) {
w *= 3;
if (i >> j - 1 & 1)
f[u][i] = max(f[u][i], f[u ^ 1][i ^ 1 << j - 1] + cnt[j] * p - w);
}
}
}
cout << f[u][(1 << 12) - 1] << endl;
}
signed main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}