2023.1.17-AtCoder Beginner Contest 281-题解

分数分布情况:

题目详解:

A - Count Down

Print all non-negative integers less than or equal to N in descending order.

题目大意:降序打印所有非负且小于等于N的数字

解题思路:设置一个变量将他自减并打印出来即可

AC代码:

#include <iostream>
using namespace std;
int main()
{
    int N;
    cin >> N;
    for (int i = N; i >= 0; i--)
    {
        cout << i << endl;
    }
}


B - Sandwich Number

题目大意:输入一个字符串s判断s是否符合以下三个条件?

1.以大写字母开头

2.还没到中间包含一个六位数的数字x。其大小在 x\in \left [ 100000,999999 \right ]

3.以大写字母结束

解题思路:读取一个字符串s的首尾字符,在判断中间的数字是否符合要求即可

#include <iostream>
using namespace std;
int main()
{
    string S;
    cin >> S;
    if (S.length() != 8)
    {
        cout << "No";
        return 0;
    }
    if (S[1] <= '0' || S[1] > '9')
    {
        cout << "No";
        return 0;
    }
    if (S[0] < 'A' || S[0] > 'Z' || S[S.length() - 1] < 'A' || S[S.length() - 1] > 'Z')
    {
        cout << "No";
        return 0;
    }
    for (long unsigned int i = 2; i < 7; i++)
    {

        if (S[i] < '0' || S[i] > '9')
        {
            cout << "No";
            return 0;
        }
    }
    cout << "Yes";
    return 0;
}


C - Circular Playlist

 题目大意:有一个播放器里面一共有n首歌。给定每一首歌的长度,每首歌的播放是互斥的并且一首播完立即播下一首。那么在一个给定的T时间之后,播放到哪一首歌了?这首歌已经播放了几秒?

解题思路:题目的描述很容易让人想到取余运算。

那么我的思路是首先观察题目给的数据

我们可以知道T是播放时间的累计,而且歌曲列表是循环的

所以把给定的时间T与歌曲总时长sum取余得到的数就是最后一次循环正在播放的歌曲的净时间。那么只需要判断在最后一次停在哪里即可,把时间从头开始累加起来就行,把时间从头开始累加起来就行,一旦这个数字超过T%sum就证明正在播放这一首歌,最后再相减就可以得到这首歌已经播放的时间。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    long long n, t, sum = 0, tmp = 0, a[100010];
    cin >> n >> t;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        sum += a[i];
    }
    for (int i = 0; i < n; i++)
    {
        tmp += a[i];
        if (tmp > t % sum)
        {
            cout << i+1 << endl;
            cout << t % sum - (tmp - a[i]) << endl;
            return 0;
        }
    }
    return 0;
}


D - Max Multiple

 题目大意:

解题思路:我们可以设假设dp[i][j][k]表示从前 i个数取出 j个数,当这些数和d取余的结果为k时和的最大值。状态转移方程就考虑第 i个数选或不选即可。

#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 1e2 + 10;
long long dp[MAXN][MAXN][MAXN], a[MAXN];
int main()
{
    int N, K, D;
    cin >> N >> K >> D;
    for (int i = 0; i < N; i++)
    {
        cin >> a[i];
    }
    memset(dp, -1, sizeof(dp));
    dp[0][0][0] = 0;
    for (int i = 0; i < N; i++)
        for (int j = 0; j <= K; j++)
            for (int k = 0; k < D; k++)
            {
                if (dp[i][j][k] == -1)
                {
                    continue;
                }
                dp[i + 1][j][k] = max(dp[i][j][k], dp[i + 1][j][k]); // 第二种情况
                if (j < K)
                {
                    dp[i + 1][j + 1][(k + a[i]) % D] = max(dp[i + 1][j + 1][(k + a[i]) % D], dp[i][j][k] + a[i]);
                } 
            }
    cout << dp[N][K][0];
    return 0;
}


E - Least Elements

 题目大意:给你一个整数数组 A,求每段长为 M的数列中前 K小的数的和

解题思路:这应该是一道滑动窗口的题目可以拿一个数据结构维护这前k小的数。当窗口滑动的时候,这里面有一些数可能因为不在窗口里而被踢掉,而有一些可能因为窗口新加进来的数更小而被踢掉(但被踢掉的数还可能拿回来),因此我们把这个窗口里的m个数用两个数据结构维护。第一个数据结构维护这 m个数中前 k小的数,而第二个数据结构维护剩下的m−k个数。第一个数据结构要求能踢掉指定元素(不在窗口内的),还要查询其最大值(与新加进来的数比较),然后实时保证其元素个数为k。而第二个数据结构则仅要求查询其最小值(踢掉的话可以在查的时候看下标是不是合法的)因此第一个数据结构可以用 集合,第二个就用优先队列。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
 
int main() {
    int n, m, k;
    cin >> n >> m >> k;
    vector<int> a(n);
    for(auto &i : a)
        cin >> i;
    priority_queue<pair<int, int>> val;
    set<pair<int, int>> pos;
    LL ans = 0;
    for(int i = 0; i < m - 1; ++ i){
        val.push({-a[i], i});
    }
    for(int i = m - 1; i < n; ++ i){
        int l = i - m + 1;
        if (l > 0){
            auto it = pos.find({a[l - 1], -(l - 1)});
            if (it != pos.end()){
                pos.erase(it);
                ans -= a[l - 1];
            }
        }
        val.push({-a[i], i});
        while(pos.size() < k){
            auto tmp = val.top();
            if (tmp.second >= l){
                pos.insert({-tmp.first, -tmp.second});
                ans += -tmp.first;
            }
            val.pop();
        }
 
        while(!val.empty()){
 
            auto tmp = val.top();
            if (tmp.second >= l && -tmp.first < pos.rbegin() -> first){
 
                val.pop();
                auto it = prev(pos.end());
                val.push({-it->first, -it->second});
                ans -= it->first;
                pos.erase(it);
                pos.insert({-tmp.first, -tmp.second});
                ans += -tmp.first;
            }else if (tmp.second < l)
                val.pop();
            else 
                break;
        }
        cout << ans << ' ';
    }
    return 0;
}


F - Xor Minimization

 题目大意:给一个n个元素的序列,现在进行一次操作,任选一个数x,对每一个元素,都异或x。问操作后序列中的最大值的最小是多少?

解题思路:这个题目看第一眼时我想到了二分查找,因为他有一个最小化最大值,后来仔细想了想,发现这题其实是一道字典树的题目,那问题就迎刃而解了。(关于字典树是什么

#include <bits/stdc++.h>
using namespace std;
int M=1e5*5;
int ch[M*30][2],tot;
int dfs(int now,int dep) {
    if(dep==-1)return 0;
    if(ch[now][0]==0)return dfs(ch[now][1],dep-1);
    if(ch[now][1]==0)return dfs(ch[now][0],dep-1);
    return min(dfs(ch[now][0],dep-1),dfs(ch[now][1],dep-1))+(1<<dep);
}
 
int main() {
    int n;
    cin >> n;
    for(int i=1;i<=n;i++) {
        int p=0;
        int x;
        cin >> x;
        for(int j=29;j>=0;j--) {
            int v=x>>j&1;
            if(ch[p][v]==0)ch[p][v]=++tot;
            p=ch[p][v];
        }
    }
    cout << dfs(0,29) << endl;
    return 0;
}


G和EX等我容先学学再写()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值