Loading

Atcoder Beginner Contest 301




B - Fill the Gaps

题目大意

给定n个数字, 要求补齐各个数字之间的数; eg: 输入1 5 2, 输出1 2 3 4 5 4 3 2;

解题思路

签到题不多嗦了

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
int q[N];
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>q[i];
        if(i>1){
            if(q[i]>q[i-1]+1){
                for(int j=q[i-1]+1;j<q[i];j++){
                    cout<<j<<' ';
                }
            }
            else if(q[i]<q[i-1]-1){
                for(int j=q[i-1]-1;j>q[i];j--){
                    cout<<j<<' ';
                }
            }
        }
        cout<<q[i]<<' ';
    }
    return 0;
}




C - AtCoder Cards

题目大意

给出两个字符串, 字符串内的字符可以随意调整顺序; 问是否有一种排序可以让两个字符串相同; 本题有个特殊规则, 字符串里的'@'字符可以被替换成'a' 't' 'c' 'o' 'd' 'e' 'r'这六个字符里的任意一个;

解题思路

我们可以先把s1和s2字符串里面各自'@'的数量存起来; 然后遍历两个字符串, 我们可以得到每个字符在s1和s2里数量的差值; eg: s1里面有1个'c', s2里面有2个'c', 那么'c'的值就是-1, 这个我们可以用map来存; 遍历完字符串之后我们接着遍历所有差值不为0的字符, 如果该字符不是atcoder里面的一个, 那么就一定配对失败; 如果是的话, 我们可以根据差值的正负来判断是用s1还是s2的'@'来抵消, 如果'@'不够就配对失败; 结束遍历之后还要再看看s1和s2剩余'@'的数量是否一致, 如果不一致自然也是配对失败;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
set<char> s;
map<char,int> mp;
signed main(){
    s.insert('a');
    s.insert('t');
    s.insert('c');
    s.insert('o');
    s.insert('d');
    s.insert('e');
    s.insert('r');
    string s1,s2;
    cin>>s1>>s2;
    int len=s1.size();
    int x=0,y=0;
    for(int i=0;i<len;i++){
        if(s1[i]=='@') x++;
        else mp[s1[i]]++; 
    }
    for(int i=0;i<len;i++){
        if(s2[i]=='@') y++;
        else mp[s2[i]]--;
    }
    bool f=true;
    for(auto t:mp){
        char a=t.first;
        int b=t.second;
        if(b!=0){
            if(s.count(a)==0){
                f=false;
                break;
            }
            else {
                if(b>0) y-=b;
                else  x+=b;
                if(x<0||y<0){
                    f=false;
                    break;
                }
            }
        }
    }
    if(x!=y) f=false;
    if(f) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}




D - Bitmask

题目大意

给定一个只由1, 0, ?组成的字符串s以及一个数字n, s可以被看作是一个数的二进制表示, ?可以被替换为0或1; 在s可能表示的所有数里面, 我们需要输出小于n的最大的数, 如果没有则输出-1;

解题思路

首先我们可以把所有?看作是0, 这样我们就可以得到s可以表示的最小的数num, 然后我们可以把每个?所代表的数存到vector里面, eg: 如果?在第4位, 那么它就代表数字8; 然后我们从大到小遍历vector, 然后不断更新num直到找到答案;
注意: 之所以从大到小遍历是因为对于二进制来说, 某一位上表示的数是它前面所有位表示的数的总和+1; 如果这一位不满足才能继续看后面的, 如果满足则必须选这一位, 因为即使把后面的全选了也不如选这一位大;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
vector<int> st;
vector<int> v;
bool cmp(int a,int b){
    return a>b;
}
signed main(){
    string s1;
    int n;
    cin>>s1>>n;
    reverse(s1.begin(),s1.end());
    int num=0;
    int a=1;
    for(int i=0;i<s1.size();i++){
        if(s1[i]=='1')  num+=a;
        else if(s1[i]=='?')  v.push_back(a);
        a=a*2;
    }
    sort(v.begin(),v.end(),cmp);
    if(num>n)  cout<<-1;
    else if(num==n) cout<<n;
    else{
        for(int x:v){
            int y=num+x;
            if(y>n) continue;
            else if(y==n){
                cout<<n;
                return 0;
            }
            else num=y;
        }
        cout<<num;
    }
    return 0;
}




E - Pac-Takahashi

题目大意

给定一个二维迷宫, '#'代表墙, 'o'代表糖果, 'S'代表起点, 'G'代表终点; 起点和终点都只有一个, 糖果不会超过18个; 给定步数t, 问在不超过t步数的情况下, 最多吃到多少个糖果

解题思路

第一眼以为是最短路问题, 结果越看越觉得事情不简单, 因为本题是可以走回头路的, 这样复杂度就大大提高了; 看了看佬的题解, 发现他们都是由最多只有18个糖果这个点想到了用状态压缩dp去求解(太强了 Orz);
对于本题的复杂情况, 可以对其进行简化, 我们只看起点到糖果, 糖果到糖果, 糖果到终点的距离就可; 对此我们可以把起点和终点都看作糖果, 然后求出每个糖果到其他各个糖果的最短距离; 这个过程我们最多用20次bfs就可以完成, 因为最多只有20个点, 所以复杂度不会很高; 对此我们需要对糖果编号, 而我们将所有起点终点以及糖果的坐标存在一个vector中, 用下标作为编号, 起点是第一个, 终点是最后一个;
f[i][j]的状态表示为, 在i的状态下我们经过第j个糖果所需要的最短路程; 而i是一个最多20位的二进制表示, 第k位上是1就表示当前状态已经经过了第k个糖果; 例如i为010011, 表示当前状态我们以及经过了第1,2,5个糖果; 注意这只是起始状态, 我们需要在此状态下继续扩展, 去找还未经过的糖果h; 注意此时还需要考虑一个问题, 即当前的起始状态下, 哪个糖果是最后一个经过的糖果j, 我们要用这个糖果去扩展其他未在起始状态的糖果, 对此我们只需要先把起始状态下的每个点都作为结尾点遍历一遍即可;
而状态计算为 f[i1][h] = min ( f[i1][h], f[i][j]+d [j][h] );
j为起始状态最后经过的糖果, h为即将由j扩展到的糖果; i为起始状态, i1为扩展到糖果h后的状态; 对于i1 我们可以用 i | ( 1 << h ) 表示;
最后再筛选一下在所有状态中, 经过终点且步数小于要求t的最小步数;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 310, M = 1048576;
char q[N][N];
int d[N][N];
int dis[N][N];
bool st[N][N];
int f[M][20];
int dx[] = { 1,0,-1,0 }, dy[] = { 0,1,0,-1 };
int n, m, k;
vector<PII> v;
void bfs(int x, int y) {
    queue<PII> qu;
    memset(d, 0x3f, sizeof d);
    memset(st, false, sizeof st);
    d[x][y] = 0;
    qu.push({ x,y });
    st[x][y] = true;
    while (qu.size()) {
        auto t = qu.front();
        qu.pop();
        for (int i = 0; i < 4; i++) {
            int a = t.first + dx[i];
            int b = t.second + dy[i];
            if (a >= 1 && a <= n && b >= 1 && b <= m && q[a][b] != '#' && !st[a][b]) {
                d[a][b] = d[t.first][t.second] + 1;
                st[a][b] = true;
                qu.push({ a,b });
            }
        }
    }
}
signed main() {
    cin >> n >> m >> k;
    int sx, sy, gx, gy;
    v.push_back({ 0,0 });
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> q[i][j];
            if (q[i][j] == 'S') {
                sx = i, sy = j;
                *v.begin() = { i,j };
            }
            else if (q[i][j] == 'G') {
                gx = i, gy = j;
            }
            else if (q[i][j] == 'o') {
                v.push_back({ i,j });
            }
        }
    }
    v.push_back({ gx,gy });
    int num = v.size();
    for (int i = 0; i < num; i++) {
        int x = v[i].first, y = v[i].second;
        bfs(x, y);
        for (int j = 0; j < num; j++) {
            int a = v[j].first;
            int b = v[j].second;
            dis[i][j] = d[a][b];
        }
    }
    memset(f, 0x3f, sizeof f);
    f[1][0] = 0;
    for (int i = 0; i < (1 << num); i++) {
        for (int j = 0; j < num; j++) {
            if (f[i][j] <= k) {
                for (int h = 0; h < num; h++) {
                    if ((i >> h) & 1) continue;
                    f[i | (1 << h)][h] = min(f[i | (1 << h)][h], f[i][j] + dis[j][h]);
                }
            }
        }
    }
    int maxn = 0;
    for (int i = 0; i < (1 << num); i++) {
        if (f[i][num - 1] <= k) {
            int res = 0;
            for (int j = 0; j < num; j++) {
                if (i >> j & 1) {
                    res++;
                }
            }
            maxn = max(maxn, res);
        }
    }
    maxn -= 2;
    if (maxn < 0) maxn = -1;
    cout << maxn;
    return 0;
}




F - Anti-DDoS

难度: ⭐⭐⭐⭐⭐⭐

题目大意

待补...

解题思路

待补...

神秘代码

//待补...
posted @ 2023-06-12 20:48  mostimali  阅读(37)  评论(0编辑  收藏  举报