比较菜只有 A ~ E

A.Erasing Zeroes

题目描述:

原题面

题目分析:

使得所有的 \(1\) 连续也就是所有的 \(1\) 中间的 \(0\) 全部去掉,也就是可以理解为第一个 \(1\) 到最后一个 \(1\) 中间的 \(0\) 全部去掉,也就是它们之间 \(0\) 的个数,那么就顺序、逆序扫一遍就出来了。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
int l = -1,r = -1;
for(int i=0; i<s.size(); i++){
if(s[i] == '1'){
l = i;
break;
}
}
for(int i=s.size(); i>=0; i--){
if(s[i] == '1'){
r = i;
break;
}
}
if(l == -1){
printf("0\n");
continue;
}
int ans = 0;
for(int i=l; i<=r; i++){
if(s[i] == '0'){
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}

B.National Project

题目描述:

原题面

题目分析:

题目要求是至少有一半是在好天气中修的,那么我们就考虑如果好天气都修那么修到哪一天可以满足这个条件。

我们可以将每 \(g\) 天视为一轮,那么总共就有 \(\lfloor\dfrac{\lceil \dfrac{n}{2} \rceil}{g}\rfloor\) 个完整的轮数,以及多出来的 \(\lceil \dfrac{n}{2} \rceil\% g\) 天。这一轮既有好天气也有坏天气,所以最后得到在哪一天可以满足条件时要算上坏天气的天数。如果多出来的天数不为 \(0\),那么就意味着这么多轮坏天气都需要经过,而如果多出来的天数为 \(0\),那么意味着经过坏天气的轮数是我们求出来的轮数减一,因为最后一轮不需要经过坏天气。

所以最后在哪一天可以满足条件的答案就是:(令 \(x = \lfloor\dfrac{\lceil \dfrac{n}{2} \rceil}{g}\rfloor\))

(1)若有多余的天数:\(x \times (g+b) + \lceil \dfrac{n}{2} \rceil\% g\)

(2)若没有多余的天数:\((x-1)\times b + x \times g\)

需要注意的是这几天只是满足好天气一半的条件,不一定可以全部修完,所以需要与 \(n\) 取 \(\max\)

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
long long t;
cin>>t;
while(t--){
long long n,g,b;
cin>>n>>g>>b;
long long x = (n+1)/2/g;
if(((n+1)/2)%g == 0){
printf("%lld\n",max((x-1) * b + x * g,n));
}
else{
printf("%lld\n",max(x * (g+b) + ((n+1)/2) % g,n));
}
}
return 0;
}

一个优美的技巧:\(\lceil \dfrac{n}{2} \rceil\) 可以写为: \(\lfloor \dfrac{n+1}{2} \rfloor\)

C.Perfect Keyboard

题目描述:

原题面

题目分析:

这种题很显然要先考虑转化为图上的问题。

很显然我们可以在 \(S\) 中相邻的两个字符之间连边,表示这两个字符必须相邻。

判断无解的条件也很明显了:

(1)某个点的度数大于等于 \(3\),因为一条连边表示一个相邻关系,不可能同时与三个字符相邻。

(2)出现大于等于 \(3\) 的环,这个也是相当显然的自己手推一下就知道不可能

那么剩下的就是一条条的链了,那么就按顺序扫过这一条链,到了某个节点就输出这个节点代表的字母就好了,为了避免重复输出应该记一个数组表示这个字母有没有输出过。也需要注意一点:要从度数为 \(1\) 的点开始扫,因为度数为 \(2\) 意味着一种中间的关系,显然只能一边边地输出无法从中间扩展。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 50;
int head[MAXN],cnt,du[MAXN],edge[MAXN][MAXN];
bool vis[MAXN],flag,use[MAXN];
void dfs(int now,int from){
vis[now] = true;
for(int i=0; i<26; i++){
if(i == from || !edge[now][i] || now == i) continue;
if(vis[i]){
flag = true;
return;
}
dfs(i,now);
}
}
void get_ans(int now,int from){
if(!vis[now])
cout<<char(now + 'a');
vis[now] = true;
for(int i=0; i<26; i++){
if(vis[i] || i == from || !edge[now][i] || now == i) continue;
get_ans(i,now);
}
}
int main(){
int t;
cin>>t;
while(t--){
memset(edge,0,sizeof(edge));
memset(vis,false,sizeof(vis));
memset(use,false,sizeof(use));
memset(du,0,sizeof(du));
cnt = 0;
flag = false;
int mx = 0;
string s;
cin>>s;
for(int i=0; i<s.size() - 1; i++){
if(!edge[s[i]-'a'][s[i+1]-'a']){
du[s[i]-'a']++;
du[s[i+1]-'a']++;
mx = max(mx,max(du[s[i]-'a'],du[s[i+1]-'a']));
}
edge[s[i]-'a'][s[i+1]-'a'] = true;
edge[s[i+1]-'a'][s[i]-'a'] = true;
use[s[i] - 'a'] = true;
use[s[i+1] - 'a'] = true;
}
for(int i=0; i<26; i++){
if(!vis[i]){
dfs(i,i);
}
if(flag)
break;
}
if(flag || mx >= 3){
printf("NO\n");
}
else{
printf("YES\n");
memset(vis,false,sizeof(vis));
for(int i=0; i<26; i++){
if(du[i] == 1)
get_ans(i,i);
}
for(int i=0; i<26; i++){
if(!vis[i])
cout<<char(i + 'a');
}
printf("\n");
}
}
return 0;
}

因为可能含有大量的重边而且点的数量很少所以可以考虑使用邻接矩阵。

输出的时候也要注意可能有的点没有出现那么就在最后输出这些没有出现的点。

所谓有大小大于等于三的环也可以理解为无向图上的有环,也就是如果从当前节点可以到达一个曾经访问过但不是其父亲的点那么就意味着有环。

D.Fill The Bag

题目描述:

原题面

题目分析:

考虑物品大小都是 \(2\) 的非负整数次幂也就可以联想到将 \(n\) 二进制拆分,因为这些物品的顺序不影响答案所以就考虑将他们存放到 \(cnt\) 数组中去。

考虑如果 \(n\) 的当前位我们数组中有那么直接减就好了,很显然这样做最优。而如果没有那么就要考虑是用比当前位小的那些数加和凑出这个位还是从比当前位更大的位拆。

那么这样就意味着我们要维护一个 \(sum\) 代表比当前位小的那些数的和,如果这个值大于当前位的值显然就扫一遍比它小的位然后减去这些位的值直到减为 \(0\) 就好。

而如果用比它小的数凑不出来当前位那么就找到比当前位大的最小的有值一位,然后从这一位一直拆,拆到当前位然后减掉就好了。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long MAXN = 3e5+5;
long long cnt[MAXN];
int main(){
long long t;
cin>>t;
while(t--){
memset(cnt,0,sizeof(cnt));
long long flag = 0;
long long n,m;
long long mx = 0;
cin>>n>>m;
for(long long i=1; i<=m; i++){
long long a;
cin>>a;
cnt[int(log2(a))]++;
mx = max(mx,a);
flag += a;
}
if(flag < n){
printf("-1\n");
continue;
}
long long sum = 0;
long long now = 0;
long long ans = 0;
while(n){
if(n & 1){
if(cnt[now]){
cnt[now]--;
}
else if(sum >= (1<<now)){ //用低位补
long long res = (1<<now);
for(long long i=now-1; i>=0 && res; i--){
if(cnt[i] * (1<<i) <= res){
sum -= cnt[i] * (1<<i);
res -= cnt[i] * (1<<i);
cnt[i] = 0;
}
else{
while(cnt[i] && (1<<i) <= res){
res -= (1<<i);
sum -= (1<<i);
cnt[i]--;
}
}
}
}
else{ //找到高位拆
for(long long j = now+1; j<=mx; j++){
if(cnt[j]){
for(long long k=j; k>now; k--){
cnt[k-1] += 2;
cnt[k]--;
ans++;
}
break;
}
}
cnt[now]--;
}
}
n>>=1;
sum += cnt[now] * (1<<now);
now++;
}
cout<<ans<<endl;
}
return 0;
}

注意一开始先判断有无解,也就是所有数加起来能不能大于等于 \(n\),若能大于等于显然有解。

E.Erase Subsequences

题目描述:

原题面

题目分析:

我们很明显可以想出来一步:枚举 \(t\) 在哪里拆开,然后将 \(t\) 转化为 \(t1+t2\),再判断 \(s\) 中能不能拆出 \(t1,t2\) 就好了。

那么问题就转化为了 \(s\) 中能不能拆出来的问题了。发现可能需要 \(dp\) 求解。

很显然的状态是:\(dp[i][j][k]\) 表示 \(s\) 的前 \(i\) 位能不能拆出 \(t1\) 的前 \(j\) 位和 \(t2\) 的前 \(k\) 位,因为状态量过大我们就考虑优化这个状态。

也就是将状态改写为:\(dp[i][j]\) 表示 \(s\) 的前 \(i\) 位拆出 \(t1\) 的前 \(j\) 位最多再拆出 \(t2\) 的前多少位。这样当 \(dp[size_s][size_{t1}] = size_{t2}\) 时也就意味着可以拆出。

那么就考虑转移,转移也就是做决策,这里的决策显然就是 \(s\) 的当前位应该放到 \(t1\) 的后面还是 \(t2\) 的后面还是都不放,这样也能保证是不相交的子序列。

(1)当 \(s[i+1] = t1[j+1]\) 时,\(dp[i][j] -> dp[i+1][j+1]\)

(2)当 \(s[i+1] = t2[f[i][j] + 1]\) 时,\(dp[i][j] + 1 -> dp[i+1][j]\)

(3)任何情况下,\(dp[i][j] -> dp[i+1][j]\),这个也十分显然

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 805;
int f[MAXN][MAXN];
bool check(string s,string t1,string t2){
memset(f,-1,sizeof(f));
f[0][0] = 0;
s = '0' + s;
t1 = '0' + t1;
t2 = '0' + t2;
for(int i=0; i<s.size(); i++){
for(int j=0; j<t1.size(); j++){
if(f[i][j] == -1) continue;
f[i+1][j] = max(f[i+1][j],f[i][j]);
if(s[i+1] == t1[j+1]) f[i+1][j+1] = max(f[i+1][j+1],f[i][j]);
if(s[i+1] == t2[f[i][j] + 1]) f[i+1][j] = max(f[i+1][j],f[i][j] + 1);
}
}
if(f[s.size()-1][t1.size()-1] == t2.size()-1)
return true;
return false;
}
bool solve(string s,string t){
for(int i=0; i<=t.size(); i++){
string t1,t2;
for(int j=0; j<i; j++){
t1 = t1 + t[j];
}
for(int j=i; j<t.size(); j++){
t2 = t2 + t[j];
}
if(check(s,t1,t2))
return true;
}
return false;
}
int main(){
int n;
cin>>n;
while(n--){
string s,t;
cin>>s>>t;
if(solve(s,t)){
printf("YES\n");
}
else{
printf("NO\n");
}
}
return 0;
}

我们会发现对于 \(dp\) 的边界、初值不好设置,那么就将所有的字符串前面加上一位那就好转移了。

【题解】Educational Codeforces Round 82的更多相关文章

  1. Educational Codeforces Round 82 (Rated for Div. 2) A-E代码(暂无记录题解)

    A. Erasing Zeroes (模拟) #include<bits/stdc++.h> using namespace std; typedef long long ll; ; in ...

  2. Educational Codeforces Round 82 (Rated for Div. 2)

    题外话 开始没看懂D题意跳了,发现F题难写又跳回来了.. 语文好差,码力好差 A 判第一个\(1\)跟最后一个\(1\)中\(0\)的个数即可 B 乘乘除除就完事了 C 用并查集判一下联通,每个联通块 ...

  3. Educational Codeforces Round 82 (Rated for Div. 2)E(DP,序列自动机)

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ],t[]; int n,m; ][]; ...

  4. Educational Codeforces Round 82 (Rated for Div. 2)D(模拟)

    从低位到高位枚举,当前位没有就去高位找到有的将其一步步拆分,当前位多余的合并到更高一位 #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h&g ...

  5. Educational Codeforces Round 82 C. Perfect Keyboard

    Polycarp wants to assemble his own keyboard. Layouts with multiple rows are too complicated for him ...

  6. Educational Codeforces Round 82 B. National Project

    Your company was appointed to lay new asphalt on the highway of length nn. You know that every day y ...

  7. Educational Codeforces Round 82 A. Erasing Zeroes

    You are given a string ss. Each character is either 0 or 1. You want all 1's in the string to form a ...

  8. [CF百场计划]#3 Educational Codeforces Round 82 (Rated for Div. 2)

    A. Erasing Zeroes Description You are given a string \(s\). Each character is either 0 or 1. You wan ...

  9. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

随机推荐

  1. Divan and bitwise operations

    这是一道比较综合的数学题目,光是吧题目看懂就花了我好一会儿时间,先看看题目吧: 题目分析:对于m段给定连续段的或值,要求出n个数的序列子序列的异或值之和: 题解: 这道题,我们先不要把它当作一个数一个 ...

  2. plicp 点云迭代最近邻点配准法

    输入参数 点云A的极坐标集合 点云A对应Lidar所在pose 点云B的极坐标集合 点云B对应Lidar所在pose Features 根据两个点云的弧度关系确定找点的起始位置 根据两个点云的弧度关系 ...

  3. ONNXRuntime学习笔记(三)

    接上一篇完成的pytorch模型训练结果,模型结构为ResNet18+fc,参数量约为11M,最终测试集Acc达到94.83%.接下来有分两个部分:导出onnx和使用onnxruntime推理. 一. ...

  4. linux fedora35 配置jdk,安装mysql,安装tomcat

    配置jdk18很简单,下载jdk,只需要.tar.gz结尾的文件就行,https://download.oracle.com/java/18/latest/jdk-18_linux-x64_bin.t ...

  5. Flutter和iOS混编详解

    前言 下面的内容是最近在使用Flutter和我们自己项目进行混编时候的一些总结以及自己踩的一些坑,处理完了就顺便把整个过程以及一些我们可能需要注意的点全都梳理出来,希望对有需要的小伙伴有点帮助,也方便 ...

  6. 记录一次用宝塔部署微信小程序Node.js后端接口代码的详细过程

    一直忙着写毕设,上一次写博客还是元旦,大半年过去了.... 后面会不断分享各种新项目的源码与技术.欢迎关注一起学习哈! 记录一次部署微信小程序Node.js后端接口代码的详细过程,使用宝塔来部署. 我 ...

  7. 第一个Python程序 | 机选彩票号码+爬取最新开奖号码

    (机选彩票号码+爬取最新开奖号码 | 2021-04-21) 学习记录,好记不如烂笔头 这个程序作用是<机选三种彩票类型的号码> 程序内包含功能有如下: 自动获取最新的三种彩票的开奖号码 ...

  8. .net 项目使用 JSON Schema

    .net 项目使用 JSON Schema 最近公司要做配置项的改造,要把appsettings.json的内容放到数据库,经过分析还是用json的方式存储最为方便,项目改动性最小,这就牵扯到一个问题 ...

  9. JS 异步与 Promise

    JS 异步与 Promise 本文写于 2020 年 6 月 8 日 1. 同步与异步与回调函数 Promise 现在是前端面试必考题呀,但是先不急着看 Promise,我们首先来看看什么是异步. - ...

  10. kernel heap bypass smep,smap && 劫持modprobe_path

    kernel heap bypass smep,smap && 劫持modprobe_path exp1 smep:smep即用户数据不可执行,当 CPU 处于 ring0 模式时,执 ...