Codeforces Round 929 (Div. 3)(D ~ G 题讲解)

Codeforces Round 929 (Div. 3)

D. Turtle Tenacity: Continual Mods

链接:Problem - D - Codeforces

题意:

给定一个数组,可以改变该数组中任意元素的位置,从左 m o d mod mod 到右,若结果不为零则输出 y e s yes yes,否则输出 n o no no

思路:

如果最小值出现的次数为一的话,那么最后的结果就不会为零,或者后面的数 m o d mod mod最小值不为零的话,结果也不会等于零

代码:

#include <iostream>
#include<algorithm>
using namespace std;
#define ll long long

const int N = 1e5 + 10;
int a[N];

void solve()
{
  int n ; cin >> n;
  for(int i = 1 ; i <= n ; i++) cin >> a[i];
  sort(a + 1 , a + n + 1);//先排序
  if(a[1] != a[2])//如果最小值只出现一次,则直接输出“YES”
  {
    cout << "YES" << endl;
    return;
  }
  else
  {
    for(int i = 2 ; i <= n ; i++)//寻找是否有值mod最小值不为零
    if(a[i] % a[1] != 0)//如果有则输出“YES”
    {
      cout << "YES" << endl;
      return;
    }
  }
  cout << "NO" << endl;//如果没有则输出“NO”
  return;
}

int main()
{
  int t ; cin >> t;
  while(t--) solve();
  return 0;
}

E. Turtle vs. Rabbit Race: Optimal Trainings

链接:Problem - E - Codeforces

题意:

给定一个数组 a a a,数组长度为 n n n,有 q q q次询问。对于每个询问,会给定左边界 l l l和一个特定值 t a r g e t target target,然后求出最小的右边界 r r r使得 l l l r r r之间的数字之和与 t a r g e t target target相差最小。

思路:

前缀和 + 二分

  1. 用一个数组存储从 1 1 1 n n n的前缀和
  2. 二分出小于 t a r g e t target target最大右边界 r r r
  3. 判断 r + 1 r + 1 r+1是不是比 r r r t a r g e t target target的差值更小,如果更小,则答案为 r + 1 r + 1 r+1,否则答案为 r r r

代码:

#include <iostream>
using namespace std;
#define ll long long

const int N = 1e5 + 10;
int a[N] , sum[N];

void solve()
{
  int n ; cin >> n;
  for(int i = 1 ; i <= n ; i++)
  {
    cin >> a[i] ;
    sum[i] = sum[i - 1] + a[i];//前缀和
  }
  int q ; cin >> q;
  while(q--)
  {
    int l , u;
    cin >> l >> u;
    int left = l , right = n;
    while(left < right)//二分出最右边答案(二分右边界的模板)
    {
      int mid = (left + right + 1) >> 1;
      if(sum[mid] - sum[l - 1] <= u) left = mid;
      else right = mid - 1;
    }
    int r = right ;
    if(r == n || u - (sum[r] - sum[l - 1]) < sum[r + 1] - sum[l - 1] - u)//判断右边界为r + 1是不是比右边界为r小
    cout << r << ' ';
    else
    cout << r + 1 << ' ';
  }
  cout << endl;
}

int main()
{
  int t ; cin >> t;
  while(t--) solve();
  return 0;
}

F. Turtle Mission: Robot and the Earthquake

链接:Problem - F - Codeforces

题意:

  1. 给定一个 n n n ∗ * m m m的二维数组,输入数据 0 0 0表示这个位置没有石头, 1 1 1表示这个位置有石头,一开始我们在左上角,然后要走到右下角(注意:编号是从 0 0 0开始,对于行来说,也就是 0 、 1 、 ⋯ 、 n − 1 0、1、\cdots、n-1 01n1对于第一个位置和最后一列都没有石头
  2. 每过一秒钟石头会往上移动一个单位,对于行来说当 x = 0 x=0 x=0时,往上移一个单位就会到达最底层也就是 x = n − 1 x=n-1 x=n1的位置,列是不能循环的
  3. 人只能往上、往右、往下走,当要走的位置有石头或者会碰到石头时,则不能往这边走
  4. 最后求人到达目的地最短的时间,如果无法到达目的地的话输出 − 1 -1 1

思路:

小知识:signed main 和 int main 的区别?

算法:广度优先搜索(bfs)

  1. 因为石头每秒都会向上移动一个单位,所以我们可以看成石头不动,目的地每秒往下走一个单位,人的移动方式如下所示:
    • 往下走:往下走两个格子
    • 往上走:不动
    • 往右走:往右下走
    • 综上所述:人最好的方式是往右下走或者是往下走两个格子
  2. 当广度优先搜索完了之后,遍历最后一列,看看当前位置与目的地的差距(这个时候可以恢复成原来的走法,就是目的地不变,人往上走或者往下走),计算找到最小值即可

代码:

#include <bits/stdc++.h>
#define int long long//将int类型定义为long long
using namespace std;

typedef pair<int , int> PII;//定义一个存储数据的pair

const int N = 1e3 + 10;
int n , m;
int g[N][N];//存储图
int dis[N][N];//用来判断这个位置是否走过,并且可以记录走到这个地方的最小步数

void solve()
{
  cin >> n >> m;
  for(int i = 0 ; i < n ; i++)
  for(int j = 0 ; j < m ; j++)
  {
    cin >> g[i][j];//输入图
    dis[i][j] = -1;//将上次走过的路线清零
  }
  
  queue<PII> q;//定义队列,用来写bfs
  q.emplace(0 , 0);//将第一个位置存进队列中
  dis[0][0] = 0;//第一个位置的步数为零

  while(q.size())//判断队列中是否还有元素,没有就说明整个图能走到的位置都走了
  {
    auto p = q.front();//把队列首个元素取出来
    q.pop();//将队列第一个元素删除
    int x = p.first , y = p.second;//将队列第一个元素用x,y坐标表示出来

    if(!g[(x + 1) % n][y] && !g[(x + 2) % n][y] && dis[(x + 2) % n][y] == -1)//当向下走两格时,判断有没有石头或者下面第二格是否走过
    {
      dis[(x + 2) % n][y] = dis[x][y] + 1;//如果可以走,则下面两格的步数为之前步数加一
      q.emplace((x + 2) % n , y);//并且将这个位置存进队列中
    }

    if(y + 1 < m && !g[(x + 1) % n][y + 1] && dis[(x + 1) % n][y + 1] == -1)//当向右下走时,判断有没有超出列的范围,并判断右下是否有石子,还要判断右下这个位置是否走过
    {
      dis[(x + 1) % n][y + 1] = dis[x][y] + 1;//如果可以走,则右下的格子的步数为之前步数加一
      q.emplace((x + 1) % n , y + 1);//并且将这个位置存进队列中
    }
  }
  int ans = 1e18;//定义最后走的步数(注意:前面将int定义为long long了,所以不会超出范围)
  for(int i = 0 ; i < n ; i++)//主要是判断最后一列,然后恢复成原来的模样,终点不变,我们向上或者向下走
  {
    if(dis[i][m - 1] == -1) continue;//如果不能走到这个位置,就跳过
    int zhong = (n - 1 + dis[i][m - 1]) % n;//可以走到这个位置,就用zhong来记录终点所在的行
    ans = min(ans , dis[i][m - 1] + min((zhong - i + n) % n , (i - zhong + n) % n));//用来判断最短的步数
    //1.前面的min是判断直线的最短路径和现在的最短路径哪个更短
    //2.后面的min是判断最后向上走离终点近还是向下走离终点近
  }
  if(ans == 1e18)//判断ans是否发生变化
  cout << -1 << endl;//如果不变,则表示最后不可能到达终点,输出'-1'
  else
  cout << ans << endl;//如果发生了变化,则表示可以到达终点,输出最小路径ans即可
}

signed main()//注意这里不能用int,会报错,可以去搜搜signed的用法
{
  int T = 1;
  cin >> T;
  while(T--) solve();
  return 0;
}

G. Turtle Magic: Royal Turtle Shell Pattern

链接: Problem - G - Codeforces

题意:

  1. 给定一个 n ∗ m n*m nm的表格,编号都是从 1 1 1开始,一共有 q q q次操作,对于每次操作,会给你三个数据,即:饼干的位置 ( x , y ) (x,y) (x,y)以及一个字符串也就是饼干的形状 s h a p e shape shape
  2. 放的饼干不能存在连续的三个饼干是一样的形状,即:横着、竖着、斜着,这三个方位上不能存在连续三个相同形状的饼干
  3. 对于第 q q q次放,输出第 q − 1 q-1 q1次时,还有多少种放法

思路:

  1. 0 0 0表示圆形饼干, 1 1 1表示方形饼干
  2. 一共有8种放法,横着和竖着各四种,如下所示:
    • 0、0、1、1
    • 0、1、1、0
    • 1、1、0、0
    • 1、0、0、1
  3. 我们可以定义一个数组 f a g [ 8 ] fag[8] fag[8]来存放这8种放法, 0 0 0表示这种放法可以存在, 1 1 1表示这种放法不能。其中第 0 0 0种放法和第 4 4 4种只是 ( x , y ) (x,y) (x,y)的位置相反(后面以此类推),所以当 i > = 4 i>=4 i>=4的时候就调换 ( x , y ) (x,y) (x,y)的位置 i i i变成 i − 4 i-4 i4
  4. 对于第 0 0 0种放法和第 1 1 1种放法其实就是 y y y往右移了 i i i个长度,所以定义了一个 g e t ( ) get() get()函数,来求第 i i i种放法,在第 0 0 0种放法下 y y y所在的位置,然后求出这个位置下所放的时 0 0 0这种饼干还是 1 1 1这种饼干
  5. 对于第 q q q次放,都用一个循环来看,是不是在第 i i i种放法下不能成功,不能成功的话就 a n s − − ans-- ans,并且把这种放法标记成不能放,防止重复减

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int get(int x , int y , int i)//get函数用来判断该饼干放的位置与第i种放法的相同位置放的形状是不是相同
{
  if(i >= 4) return get(y , x , i - 4);//如果放的放法数是大于等于4次的(就是按照列来放),就可以把他换成横着的
  y += i;//表示在这种放发下,所在原图上的位置
  return (x & 1) ^ (y % 4 == 1 || y % 4 == 2);
  //(x & 1) 判断是奇数行还是偶数行,因为奇数行和偶数行刚好相反
  //异或上后面的数,因为一开始1、2号位上的数都为0,如果y向前折叠之后在这两个位置上就是1,然后1^1就是0
  //即:在(x , y)这个位置上放饼干在第i中放法的位置上应该是什么样饼干
}

void solve()
{
  int n , m , q;
  cin >> n >> m >> q;
  int ans = 8;//不管n,m多大,最多的次数都是8次
  cout << ans << endl;//什么都没放的时候就是8次
  int fag[8] = {0};//定义一个数组,用来存放这8中放发,哪种不能放(0:表示能放,1:表示不能放)
  while(q--)//一共放q次,输出第q次放了之后,还有多少种放法
  {
    int x , y;
    string s;
    cin >> x >> y >> s;
    int k = s[0] == 'c';//k用来记录放入的数是圆形还是方形

    for(int i = 0 ; i < 8 ; i++)//8中方法
    {
      if(!fag[i] && get(x , y , i) != k)//判断这种方法之前是不是能放,并且判断这个位置上的数是不是与这种放法的数不相同
      {
        ans--;//如果不相同,则总共的放的数减减
        fag[i] = 1;//并且把这种放法标记成不能放,防止重复减
      }
    }
    cout << ans << endl;//最后输出第q次放了之后还有多少中放法
  }
}

signed main()
{
  int t;
  cin >> t;
  while(t--) solve();
  return 0;
}
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值