2022牛客冬令营 第三场 题解
作者:互联网
A题 智乃的Hello XXXX(签到)
输出 \(\text{hello xxx}\),其中 xxx 可以是任意字符串
随便输出个啥来签下到就行,不过还是建议跟一个 ASCII码构成的字符串,防止乱码。
print("hello world")
B题 智乃买瓜(01背包)
现在有 \(n\) 个瓜,第 \(i\) 个瓜的质量为 \(w_i\)。
现在,对于每个瓜,我们可以选择:
- 买下这个瓜
- 把瓜劈一半,然后只买一半
- 不买
现在我们想要知道,当我们买的瓜的质量和为 \(1,2,\cdots,M\) 时,分别有几种方案?
\(0\leq n\leq 10^3,1\leq M \leq10^3,2\leq w_i\leq 2*10^3\),保证 \(w_i\) 为偶数
不劈瓜的话就是典型 01 背包统计方案数问题,劈瓜的话其实也差不多。
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1010;
int n, m, w[N];
int dp[N][N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> w[i];
dp[0][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 0; j <= m; ++j) {
dp[i][j] = dp[i - 1][j];
if (j >= w[i])
dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i]]) % mod;
if (j >= w[i] / 2)
dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i] / 2]) % mod;
}
for (int i = 1; i <= m; ++i)
printf("%d ", dp[n][i]);
return 0;
}
C题 智乃买瓜(another version) (逆DP)
本题和上面同一背景,不过这题是给定购买质量和为 \(1,2,\cdots,M\) 时候的方案数(对 \(10^9+7\) 取模),让我们反过来构造出这些瓜的质量。
\(1\leq M \leq 10^3\)
对于给定的方案数,我们显然能找到某个物品的 \(w\):倘若质量和为 \(x\) 的方案数大于 0,而质量和小于 \(x\) 的方案数均为 0,那么说明必然有一个质量为 \(2x\) 的瓜,我们将其存入答案数组中。
我们知道 B 题的 DP 方程为:
\[dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-w_i}+dp_{i-1,j-\frac{w_i}{2}} \]那么移项并变换,得
\[dp_{i-1,j}=dp_{i,j}-dp_{i-1,j-w_i}-dp_{i-1,j-\frac{w_i}{2}}\\ dp_{i,j}=dp_{i + 1,j}-dp_{i,j-w_{i+1}}-dp_{i,j-\frac{w_{i+1}}{2}} \]这样,我们得到了原 DP 方程的逆 DP 方程,照着推即可。
当整个 DP 数组变为全 0 时,结束流程,输出答案数组中记录的数即可。
#include<bits/stdc++.h>
using namespace std;
const int M = 1010;
const int mod = 1e9 + 7;
int m, dp[2][M];
vector<int> ans;
int main()
{
dp[0][0] = dp[1][0] = 1;
cin >> m;
for (int i = 1; i <= m; ++i) cin >> dp[0][i];
for (int i = 1; ;++i) {
int w = -1;
for (int j = 1; j <= m && w == -1; ++j)
if (dp[(i - 1) % 2][j]) w = 2 * j;
if (w == -1) break;
ans.push_back(w);
for (int j = 1; j <= m; ++j) {
dp[i % 2][j] = dp[(i - 1) % 2][j];
if (j >= w / 2) {
dp[i % 2][j] -= dp[i % 2][j - w / 2];
//减的时候如果变成负数,要加上mod,下面同理
if (dp[i % 2][j] < 0) dp[i % 2][j] += mod;
}
if (j >= w) {
dp[i % 2][j] -= dp[i % 2][j - w];
if (dp[i % 2][j] < 0) dp[i % 2][j] += mod;
}
}
}
cout << ans.size() << endl;
for (int w : ans) cout << w << ' ';
return 0;
}
D题 智乃的01串打乱(签到)
把一个纯 01 串打乱,保证字符串并非全 0 或全 1。
找到一个 1 和 一个 0 的位置,交换一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
char s[N];
int main()
{
scanf("%d%s", &n, s + 1);
for (int i = 1; i < n; ++i)
if (s[i] != s[i + 1]) {
swap(s[i], s[i + 1]);
break;
}
puts(s + 1);
return 0;
}
E题 智乃的数字积木(easy version)(排序,模拟)
现在有 \(n\) 块积木,每块积木有一个一位数字和一个颜色。相邻两个颜色相同的积木可以更换位置。
现在我们有油漆,第 \(i\) 次可以将 \(p_i\) 颜色的积木全部涂成 \(p_i\)。
问:在 \(k\) 次涂油漆后,每次分别都可以将积木排列出的最大值(从左往右读出来的十位数,允许前导 0)是多少?(包括最初情况和每次涂油漆之后的情况)
\(1\leq n,m\leq 10^5,0\leq k\leq 10\)
突然发现,每个积木只会在自己所在的那个颜色区块里面,那么直接找出来,然后硬排序即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
#define LL long long
const LL mod = 1e9 + 7;
int n, m, k;
struct Node {
int color, num;
bool operator < (const Node &rhs) const {
return num > rhs.num;
}
} a[N];
//
void change(int p, int q) {
for (int i = 1; i <= n; ++i)
if (a[i].color == p) a[i].color = q;
}
Node t[N];
LL solve() {
for (int i = 1; i <= n; ++i) t[i] = a[i];
//
int L = 1;
while (L <= n) {
int R = L;
while (R <= n && t[R].color == t[L].color) R++;
sort(t + L, t + R);
L = R;
}
LL res = 0;
for (int i = 1; i <= n; ++i)
res = (res * 10 + t[i].num) % mod;
return res;
}
char s[N];
int main()
{
//read
scanf("%d%d%d", &n, &m, &k);
scanf("%s", s + 1);
for (int i = 1; i <= n; ++i)
a[i].num = s[i] - '0';
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i].color);
//solve
cout << solve() << endl;
for (int i = 1; i <= k; ++i) {
int p, q;
scanf("%d%d", &p, &q);
change(p, q);
cout << solve() << endl;
}
return 0;
}
G题 智乃的树旋转(easy version)(模拟)
给定两个节点为 \(n\) 的二叉树,问能否通过多次旋转将前一棵二叉树变成后者?
\(n\leq 10^3\),保证答案不超过 1
保证答案不超过 1,\(n\) 的规模也不大,那就相当于考一手模拟旋转了(平衡树的知识忘得一干二净了属于是)
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n;
struct Node {
int l, r;
} a[N], b[N], t[N];
int fa[N];
int check(int x) {
return a[fa[x]].r == x;
}
void zag(int &B) {
int A = fa[B], beta = t[B].l;
t[A].r = beta, t[B].l = A;
int X = fa[A];
if (X) {
if (t[X].l == A) t[X].l = B;
else t[X].r = B;
}
}
void zig(int &A) {
int B = fa[A], beta = t[A].r;
t[B].l = beta, t[A].r = B;
int X = fa[B];
if (X) {
if (t[X].l == B) t[X].l = A;
else t[X].r = A;
}
}
void rorate(int x) {
for (int i = 1; i <= n; ++i) t[i] = a[i];
check(x) ? zag(x) : zig(x);
}
bool isAns() {
int flag = true;
for (int i = 1; i <= n && flag; ++i)
if (t[i].l != b[i].l || t[i].r != b[i].r)
flag = false;
return flag;
}
int main()
{
//read
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> b[i].l >> b[i].r;
for (int i = 1; i <= n; ++i)
cin >> a[i].l >> a[i].r;
//solve
//k=0
for (int i = 1; i <= n; ++i)
t[i] = a[i];
if (isAns()) {
puts("0");
return 0;
}
//k=1
for (int i = 1; i <= n; ++i) {
int l = a[i].l, r = a[i].r;
if (l) fa[l] = i;
if (r) fa[r] = i;
}
for (int i = 1; i <= n; ++i)
if (fa[i]) {
rorate(i);
if (isAns()) {
printf("1\n%d", i);
return 0;
}
}
return 0;
}
当然,还有一种不需要旋转的方式:如果两个节点 \(i,j\) 在两棵树里面互为父子,那么肯定是旋转的他们,这时候输出原本是父亲的那个节点。
I题 智乃的密码(双指针)
给定一个长度为 \(n\) 的字符串,完全由大写字母,小写字母,数字和特殊字符构成。现在,我们想要选一个子串当密码,且要求:
- 密码长度在 \([L,R]\) 内
- 密码里面起码含三种及以上字符
\(1\leq n\leq 10^5,1\leq L\leq R\leq n\)
带限制的双指针,挺朴素的,就是调起来有点头疼。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, L, R;
char s[N];
//
int check(int c) {
if (c >= 'A' && c <= 'Z') return 1;
if (c >= 'a' && c <= 'z') return 2;
if (c >= '0' && c <= '9') return 3;
return 4;
}
int type[N];
int main()
{
scanf("%d%d%d%s", &n, &L, &R, s + 1);
for (int i = 1; i <= n; ++i)
type[i] = check(s[i]);
long long ans = 0;
int vis[5] = {0, 0, 0, 0, 0};
int l = 1, r = 0, cnt_type = 0;
while (l <= n) {
while (r < n && cnt_type < 3) {
r++;
vis[type[r]]++;
if (vis[type[r]] == 1) cnt_type++;
}
//
if (r == n && cnt_type < 3) break;
while (l < r && cnt_type >= 3) {
ans += max(min(n, l + R - 1) - max(r, l + L - 1) + 1, 0);
vis[type[l]]--;
if (vis[type[l]] == 0) cnt_type--;
l++;
}
}
cout << ans;
return 0;
}
L题 智乃的数据库(模拟,map)
详情见题目
我们先用各种手段将指令里面那几个需要的字符串分离出来(substr 之后再 split,确实烦),然后直接模拟就是了(一切关于字符串的模拟,统统上 map,永远的神)
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
string title[N];
map<string, int> vis;
int Data[N][N];
vector<string> split_cmd()
{
string cmd;
getline(cin, cmd);
getline(cin, cmd);
string str = cmd.substr(36, cmd.length() - 37);
vector<string> res;
if (str == "") return res;
string delim = ",";
char *strs = new char[str.length() + 1];
strcpy(strs, str.c_str());
char *d = new char[delim.length() + 1];
strcpy(d, delim.c_str());
char *p = strtok(strs, d);
while (p) {
string s = p;
res.push_back(s);
p = strtok(NULL, d);
}
return res;
}
string build(int id, vector<string> &strs) {
string res = "";
for (string str : strs) {
int dat = Data[id][vis[str]];
if (res != "") res += ",";
res += to_string(dat);
}
return res;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i)
cin >> title[i];
for (int i = 1; i <= n; ++i) vis[title[i]] = i;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> Data[i][j];
//
vector<string> id = split_cmd();
map<string, int> ans;
for (int i = 1; i <= n; ++i)
ans[build(i, id)]++;
//output
cout << ans.size() << endl;
for (auto x : ans)
cout << x.second << " ";
return 0;
}
标签:const,string,冬令营,int,题解,leq,第三场,res,dp 来源: https://www.cnblogs.com/cyhforlight/p/15863536.html