Codeforces Round #627 (Div. 3)

1324A - Yet Another Tetris Problem(思维)

在这里插入图片描述
在这里插入图片描述

题意

  • 给一个数组,每一个数组中的元素大小表示在竖直方向的方块数量,元素相邻怎竖直方向的方块也相邻,类似于俄罗斯方块当底层被堆满的时候,那么那一层可以被消去,然后我们可以在任意一个元素(数列)上 + 2 两个小方块,可以放任意多次数,问最终能不能通过这些操作能不能把所有的小方块消去。

思路

  • 思路:我们可以先把底层的能消除的消除,如果消除之后 所给的数组元素中还有奇数,那么无论怎么 操作 都不能 消除完

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<map>
 
using namespace std;
#define ll long long 
#define db double
 
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    //freopen("A.txt","r",stdin);
    int t;    
    cin >> t;
    while(t --)
    {
        int n;
        cin >> n;
        int ar[105];
        int mn = 1e9;
        for(int i = 1; i <= n; i ++)
            cin >> ar[i], mn = min(mn, ar[i]);
        int have_odd = 0;
        for(int i = 1; i <= n; i ++)
        {
            ar[i] -= mn;
            if(ar[i] % 2 != 0)
            {
                have_odd = 1;
                break;
            }      
        }
 
        if(have_odd)
            cout << "NO\n";
        else
            cout << "YES\n";
    }
 
    return 0;
}
  • 收获:就是一个简单的观察

1324B - Yet Another Palindrome Problem(思维)

题意

  • 给一个序列,问在这个子序列中能否找到一个 回文 子串。

思路

  • 这一题我们可以用 map 的特性去遍历一遍所给的数组,同在这个序列中都有哪些 数字出现过,并且记录第一次出现的位置,这样我们在从右自左遍历这个序列,我们没遍历到一个元素 我们就 用map看看是否这个 数字已经出现过了,如果没有,就去往后遍历,如果有 我们就判断两个 两个相同的数组出翔位置,的间隔差 是否大于1,如果大于,那么这两个相同的数就可以与这两个位置中的任意一个数组成一个 回文子串了;如果小于等于1 没法组成会微,继续向下遍历吧

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<map>
 
using namespace std;
#define ll long long 
#define db double
 
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    //freopen("A.txt","r",stdin);
    int t;    
    cin >> t;
    while(t --)
    {
        int n;
        cin >> n;
        map<int, int> mp;
        int ar[50005];
        for(int i = 1; i <= n; i ++)    
        {
            cin >> ar[i];
            if(! mp[ar[i]])
            {
                mp[ar[i]] = i;
            }
        }
        int flag = 0;
        for(int i = n; i > 1; i --)
        {
            if(mp[ar[i]] && i - mp[ar[i]] > 1)
            {
                flag = 1;
                break;
            }
        }
        if(! flag)
            cout << "NO\n";
        else
            cout << "YES\n";
    }
 
 
    return 0;
}
  • 收获:读好题,利用好map 的记录某个一个状态、某个数的位置/出现过没有,当我们把某个数带入map中之后,同过判读是否为0,就可以判读啊某个状态、位置、出现过没没有,通过一前一后呼应 来做题

1324C - Frog Jumps

在这里插入图片描述

题意

  • 给我们一个长度为 n有 ‘L、R’ 字符组成的序列,序列下标从 1 开始,一个青wa在该序列的下标0位置,想要到下标n+1位置,开始的时候 🐸可以跳到 1~n中的任意一个表格上,之后当青蛙再次起跳时,要根据脚下的字符所代表的方向跳,并且每次 起跳的距离都要小于等于之前跳的距离(第一次跳到距离),问青蛙要想到 n+1 位置需要的最小跳距离是多少

思路(贪心)

  • 分析: 这一题我们逆向到角度来看🐸跳过程,我们假设 🐸在n+1位置想要到 0位置去,如果🐸脚下的字符为 R 则想左跳,如果为L🐸向右跳,(其实就是跟正向完全相反的过程),那么这个时候到最小跳明显是在 n+1位置向左的第一次起跳且必须跳到字符为R的位置,设置个距离为 k,接下来贪心的思想去解决问题,我们假设下一次起跳 既可以跳到 ‘R’字符的位置,又可以跳动‘L’字符的位置,那么我们怎么跳呢,假如我们跳到 L字符处,那么接下来我们要向 右跳(向后跳),但是当我跳完之后,不要忘了我们最终的目的地还是 0 位置,这样之后没有必要向右(后)继续跳了,继续向右跳制只会造成接下要想在跳回左边需要更大跳跃距离(可能这个距离已经大于了k),所以这样事不划算的;那么不如我们这样想:当我们在跳完第一个跳之后这个时候的最小跳的距离为k,在以后的跳的过程中,我们一直向左跳,也就是一直跳到字符为R的 方格,如果当前的k距离可以跳到下一R字符,我们就继续跳就行了,否怎看跳到下一个 R字符到花费是多少并距离更新 k值,,,,
  • 在算法开始时的时候我们可以,对所给的数组进行一些变化:1. 数组逆序过来(可略)2把逆序过的数组下标为0的位置设置为R

代码

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
#define ll long long 
const int Len = 2e5 + 10;
ll ar[Len], br[Len];
 
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    //freopen("A.txt","r",stdin);
    int t;
    cin >> t;
    while(t --)
    {
        string s;
        cin >> s;
        char ar[s.size() + 10];
        for(int i = s.size() - 1, j = 1; i >= 0; i --, j ++)
            ar[j] = s[i];
 
        ar[0] = ar[s.size()+1] = 'R';
        
        int last = 0;
        int l = 1;
        int ans = 0;
        while(l <= s.size())
        {
            for(int i = last + 1; i <= s.size() + 1; i ++)
            {
                if(ar[i] == 'R')
                {
                    l = i;
                    break;
                }
            }
            ans = max(ans, l - last);
            last = l;
        }
        cout << ans << endl;
    }
 
 
    return 0;
}

  • 收获:仔细分析🐸跳跃的过程,好好的利用贪心的思路,看看每一跳应该往哪跳,每一跳的关系

1324D - Pair of Topics

题意

给我们两个 长度为n 的序列 a 1 a 2 a 3 . . . a n a_{1} a_{2} a_{3} ... a_{n} a1a2a3...an b 1 b 2 b 3 . . . b n b_{1} b_{2} b_{3} ... b_{n} b1b2b3...bn 问 在 i < j 的 情 况 下 , a i + a j > a i + a j 的 组 合 有 多 少 对 i 、 j 满 足 前 面 这 个 不 等 式 问在 i < j的情况下,a_{i} + a_{j} > a_{i} + a_{j}的组合有多少对i、j满足前面这个不等式 i<jai+aj>ai+ajij 思 路 : 我 们 可 以 对 上 面 的 不 等 式 进 行 变 换 为 : a i − b i + a j − b j > 0 , 那 么 我 们 假 设 c i = a i − a j , 那 么 我 们 就 让 原 来 的 两 个 数 组 变 成 了 一 个 数 组 的 关 系 : c i − c j > 0 , 变 成 一 个 数 组 就 好 处 理 多 了 , 剩 下 的 就 看 代 码 吧 。 。 。 思路:我们可以对上面的不等式进行变换为:a_{i} - b_{i} + a_{j} - b_{j} > 0, 那么我们假设 c_{i} = a_{i} - a{j}, 那么我们就让原来的两个数组 变成了 一个数组的关系: c_{i} - c_{j} > 0 ,变成一个数组就好处理多了,剩下的就看代码吧。。。 aibi+ajbj>0,ci=aiaj,cicj>0

代码

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long 
const int Len = 2e5 + 10;
ll ar[Len], br[Len];
 
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    //freopen("A.txt","r",stdin);
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> ar[i];
    for(int i = 1; i <= n; i ++)
        cin >> br[i], ar[i] -= br[i];
    
    sort(ar + 1, ar + 1 + n);
    ll l = 1, r = n;
    ll ans = 0;
    while(l < r)
    {
        if(ar[l] + ar[r] > 0)
            ans += r - l, r --;
        else 
            l ++;
    }
    cout << ans << endl;
 
    return 0;
}
  • 收获

1324E - Sleeping Schedule (动态规划 + 细节)

题意

一个人一天睡 n 次觉,每一天有h小时,在 l~r的时间段内 为最好时间段,在刚开始睡觉开始的时间为0时, 在 第 i 次 的 时 候 可 以 选 择 在 a i 小 时 后 入 睡 , 也 可 以 在 a i − 1 小 时 后 入 睡 , 问 在 n 次 睡 觉 中 最 多 有 几 次 在 l ~ r 区 间 内 入 睡 的 在第i次的时候可以选择在 a_{i}小时后入睡,也可以在 a_{i}-1 小时后入睡,问在n次睡觉中 最多有几次在l~r区间内入睡的 iaiai1nlr

思路

  • 很明显这是一道类似于背包问题的dp问题,在第 i 次进入睡眠的时候,我们是从i-1次进入睡眠的的基础上推倒过来的,那么我们可以推导出状态转移方程:dp[i][j] = max(dp[i][j], max(dp[i - 1][ (j - ar[i] + h) %h + dp[i - 1][ (j - ar[i] + 1 + h) %h ] ) ) + check()
    其中: j 是指 第j时,

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
 
const int mxn = 2e3 + 10;
int ar[mxn];
int dp[mxn][mxn];
 
 
 
int main()
{
    //freopen("A.txt","r",stdin);
    int n, h, l, r;
    scanf("%d %d %d %d", &n, &h, &l, &r);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &ar[i]);
 
    //一开始初始化为-1不要忘了,因为不是所有的点都可以转到其实状态上来
    memset(dp, -1, sizeof(dp));
    int c1,c2;
    c1 = ar[1];
    c2 = ar[1] - 1;
    //这里一开始,要么方案数是0,要么位于最优睡觉区间上是1
    dp[1][c1] = (c1 >= l && c1 <= r) ? 1 : 0;
    dp[1][c2] = (c2 >= l && c2 <= r) ? 1 : 0;
    int ans = max(dp[1][c1], dp[1][c2]);
    for(int i = 2; i <= n; i ++)
        for(int j = 0; j <= h; j ++)
        {
            c1 = (j - ar[i] + h) % h;
            c2 = (j - ar[i] + 1 + h) % h;
            if(dp[i - 1][c1] < 0 && dp[i - 1][c2] < 0) continue;
            dp[i][j] = max(dp[i][j], max(dp[i - 1][c1], dp[i - 1][c2])) + (j >= l && j <= r ? 1 : 0);
        }
    //这里的for循环是用来判断,数据是否只有一组,如果不是找到最优方案数
    for(int i = 0; i < h; i ++)
        ans = max(ans, dp[n][i]);
    printf("%d\n", ans);
 
    return 0;
}

  • 收获:多做一点dp题,做得时候思路少的可怜。。。。。。。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值