Codeforces Round #598 (Div. 3) A~F题解

A. Payment Without Change

  • 题意
    有价值为 n n n的硬币,你可以选择 [ 0 , a ] [0,a] [0,a]个,价值为 1 1 1的硬币,你可以选择 [ 0 , b ] [0,b] [0,b]个,问你能否凑成金额 s s s

  • 解题思路
    我们易知,当用 1 1 1的时候即是拿来补 s % n s\%n s%n,而当 n × a + b n\times a+b n×a+b构成的金额 ≥ s ≥s s时,且 s m o d    n ≤ b s\mod n\leq b smodnb即可凑成。

  • AC代码

/**
  *@filename:A
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 12:50
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t;
ll a,b,n,s;
void solve(){
    if(s % n <= b && n * a + b >= s){
        cout << "YES" << endl;
    }
    else{
        cout << "NO" << endl;
    }
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> a >> b >> n >> s;
        solve();
    } 
    return 0;
}

B. Minimize the Permutation

  • 题意
    给你一个 1 1 1~ n n n的排列 a a a,你可以进行 n − 1 n-1 n1次操作,其中第 i i i次操作可以交换 a i , a i + 1 a_i,a_{i+1} ai,ai+1,求能得到的字典序最小的排列。

  • 解题思路
    贪心,自然可以操作 n − 1 n-1 n1次,所以实际上我们可以将 1 1 1放最前面,且由于操作的限制,所以我们可以先让 1 1 1往左移,让其回到第一个位置,然后遍历接下来的每一个 2 , 3... 2,3... 2,3...,进行交换操作。注意在这个过程中,如果我们 i i i已经确定了回到了它的位置,那么该点的操作就该禁用。具体看 A C AC AC代码。

  • AC代码

/**
  *@filename:B
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 12:56
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100 + 5;
const int P = 1e9+7;

int t,n,a[N],pos[N],vis[N];//vis[i]表示第i次操作是否用完。
void solve(){
    //我们会交换k-1次,首先交换1,让1往左移。再2,3,4..
    for(int i = 1; i <= n; ++ i){
        //要将pos[i]上的i回到i上。
        if(pos[i] > i){
            while(pos[i] > i && !vis[pos[i] - 1]){
                swap(a[pos[i] - 1],a[pos[i]]);
                vis[pos[i] - 1] = true;
                //cout << a[pos[i] - 1] << " " << a[pos[i]] << endl;
                //这里它们的下标都发生了改变。
                swap(pos[a[pos[i]]],pos[a[pos[i] - 1]]);
            }
        }
        vis[pos[i]] = true;//由于该点被确定了,故设置该点为true。
    }
    for(int i = 1; i <= n; ++ i){
        cout << a[i] << " ";
    }
    cout << endl;
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> n;
        memset(vis,0,sizeof(vis));
        for(int i = 1; i <= n; ++ i){
            cin >> a[i];
            pos[a[i]] = i;
        }
        solve();
    }
    return 0;
}

C. Platforms Jumping

  • 题意
    有一条长度为 n n n的河,其被均分成 n n n个单元格,且坐标为 1 1 1~ n n n。然后有 m m m块长度为 c i c_i ci的木板,你最初在 0 0 0位置,目的到达 n + 1 n+1 n+1,假设你在 x x x位置,你可以跳到 [ x + 1 , x + d ] [x+1,x+d] [x+1,x+d]上。问你能否合理的安排木板位置使其不掉落水中。

  • 解题思路
    我们可以先将木板按顺序放入右边,保证它们是连续的,然后我们根据情况往左平移木板,看是否符合。即我们从 0 0 0位置开始模拟,判断下一次跳跃的最大距离能否到木板上,如果不行,则移动到边界位置即可,然后更新 x x x。具体看AC代码。

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 15:39
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1000 + 5;
const int P = 1e9+7;

int n,m,d,c[N],pos[N];
int ans = 0;
void solve(){
    for(int i = n - ans + 1,j = 1; j <= m; ++ j){
        pos[j] = i;//获取每个木板放置的起始位置。
        i += c[j];
    }
    int idx = 1,x = 0;//idx为当前处理的模板,x为当前的坐标值。
    bool flag = false;//判断是否可行。
    while(x < n && idx <= m){
        if(x + d>= n + 1){
            //说明再跨一下可以到达。
        }
        if(x + d < pos[idx]){
            //说明不够,我们需要往前移。
            pos[idx] = x + d;
            x = pos[idx] + c[idx] - 1;
            idx ++;
        }
        else{
            break;
        }
        if(x + d <= n && idx > m){
            flag = true;
            break;
        }
    }
    if(flag){
        cout << "NO" << endl;
    }
    else{
        cout << "YES" << endl;
        for(int i = 1, j = 1; i <= n; ++ i){
            if(i == pos[j]){
                while(i <= pos[j] + c[j] - 1){
                    cout << j << " ";
                    i ++;
                }
                i --;
                j ++;
            }
            else{
                cout << 0 << " ";
            }
        }
        cout << endl;
    }
}
int main(){
    cin >> n >> m >> d;
    for(int i = 1; i <= m; ++ i){
        cin >> c[i];
        ans += c[i];
    }
    solve();
    return 0;
}

D. Binary String Minimizing

  • 题意
    给你一个 01 01 01字符串,你可以进行 k k k次交换相邻元素的操作,问你能获取的最小字典序字符串是什么?

  • 解题思路
    贪心。我们尽可能的将 0 0 0往前放,所以我们将 0 0 0的位置提取出来,按顺序依次向左交换即可。

  • AC代码

/**
  *@filename:D
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 15:47
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n;
ll k;
string s;
queue<int> pos;
void solve(){
    while(!pos.empty())pos.pop();
    for(int i = 0; i < n; ++ i){
        if(s[i] == '0')pos.push(i);
    }
    //每次遇见0往前放。
    int last = 0;
    while(!pos.empty() && k > 0){
        int idx = pos.front();
        pos.pop();
        if(idx - last < k){
            k -= (idx - last);
        }
        else{
            last = idx - k;
            k = 0;
        }
        swap(s[last],s[idx]);
        last ++;
    }
    cout << s << endl;
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> n >> k >> s;
        solve();
    }
    return 0;
}

E. Yet Another Division Into Teams

  • 题意
    给你 n n n个人的能力值 a a a,你需要将它们分组,每组最少 3 3 3个人,其组的贡献为 max ⁡ g r o u p a − min ⁡ g r o u p a \max_{group}{a}-\min_{group}{a} maxgroupamingroupa,你需要设计一种分组方案,使得组的所有贡献总和最小。

  • 解题思路
    我们最小分 3 3 3人,但最大就分 5 5 5人,因为当有 6 6 6人时,我们可以分成 2 2 2 3 3 3人一组,这样的贡献相同甚至更小。我们可以用 d p [ i ] dp[i] dp[i]来表示前 i i i个人分完组后的差的和,然后我们枚举最后一组的人数,那么状态转移方程则可以列写了: d p [ i + l e n − 1 ] = m i n { d p [ i + l e n − 1 , d p [ i − 1 ] + d i f f } dp[i+len-1]=min\{dp[i+len-1,dp[i-1]+diff\} dp[i+len1]=min{dp[i+len1,dp[i1]+diff},需要注意的是,为了计算出组内最大值和最小值,我们需要先对所有人的能力值进行排序,这也是贪心的方案,因为这样按顺序分组落差是正常的,我们同样要记录它们的位置以及每组的区间,这样,我们才可以知道每个人是分在哪个组里。具体看 A C AC AC代码。

  • AC代码

/**
  *@filename:E
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 18:11
**/
#include <bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 200000 + 5;
const int P = 1e9+7;
const int INF = 0x3f3f3f3f;

int n;
pii a[N];
int dp[N];//dp[i]表示前i个数分完组后的差的和。
int t[N],p[N];
void solve(){
    sort(a + 1, a + 1 + n);
    //需要注意i从
    for(int i = 1; i <= n; ++ i){
        dp[i] = INF;
    }
    for(int i = 1; i <= n; ++ i){
        //枚举第i个数属于的区间长度。
        for(int len = 3; len <= 5 && i + len - 1 <= n; ++ len){
            int diff = a[i + len - 1].x - a[i].x;//获取差值。
            if(dp[i + len - 1] > dp[i - 1] + diff){
                dp[i + len - 1] = dp[i - 1] + diff;
                p[i + len - 1] = i;//为上界,即从[i,i + j - 1]的区间。
                //cout << "i:" << i << " len:" << len << " value:" << dp[i + len - 1] << endl;
            }
        }
    }
    int cur = n,cnt = 0;
    while(cur != 0){
        cnt ++;
        for(int i = cur; i >= p[cur]; -- i){
            //遍历区间。
            t[a[i].y] = cnt;
        }    
        cur = p[cur] - 1;//获取下一个区间。
    }
    printf("%d %d\n", dp[n], cnt);
    for(int i = 1; i <= n; ++ i){
        cout << t[i] << " ";
    }
    cout << endl;
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &a[i].x);
        a[i].y = i;
    }
    solve();
    return 0;
}

F. Equalizing Two Strings

  • 题意
    给你两个字符串 s 1 , s 2 s_1,s_2 s1,s2,你可以选取一段区间 l e n len len,然后使得 s 1 s_1 s1 s 2 s_2 s2中的长度为 l e n len len的连续子字符串翻转。假设你可以进行无数次操作,问你能否使得 s 1 s_1 s1 s 2 s_2 s2相等。

  • 解题思路
    首先我们知道,当 l e n = 2 len =2 len=2时,实际上我们进行的翻转操作就是交换操作了。那么相等的充分条件就是 s 1 s_1 s1 s 2 s_2 s2出现的字符类型和数量要相等,经过了这一个条件就是,我们发现,如果一个字符串中的字符数量出现超过一次,那么我们就可以对其中一个字符串进行无意义的操作,然后使另一个字符串进行冒泡匹配该字符串,最后一定会相等。那么再看,如果都只出现了一次,说明该字符串长度最多 26 26 26,所以我们可以进行这匹配过程,思路是匹配 s 1 s_1 s1的前 n − 2 n-2 n2个字符,然后对于 s 1 s_1 s1的后两个字符串进行翻转,进行无意义的操作,对 s 2 s_2 s2进行冒泡匹配,当匹配完前 n − 2 n-2 n2个字符后,判断后两个字符是否相等即可。

  • AC代码

/**
  *@filename:F
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-28 20:34
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n;
string s1,s2;
int cnt1[26],cnt2[26];
void solve(){
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    for(int i = 0; i < n; ++ i){
        cnt1[s1[i] - 'a'] ++;
        cnt2[s2[i] - 'a'] ++;
    }
    bool flag = false;
    for(int i = 0; i < 26; ++ i){
        if(cnt1[i] != cnt2[i]){
            flag = true;
            break;
        }
    }
    if(flag){
        cout << "NO" << endl;
    }
    else{
        for(int i = 0; i < 26; ++ i){
            if(cnt1[i] >= 2){
                flag = true;
                break;
            }
        }
        if(flag){
            cout << "YES" << endl;
        }
        else{
            //最后判断交换情况,我们模拟交换,即构造前n - 2个,然后利用t中的最后两个一直轮换。
            for(int i = 0; i < n - 2; ++ i){
                int pos = -1;
                for(int j = 0; j < n; ++ j){
                    if(s1[i] == s2[j]){
                        pos = j;
                        break;//获取第一个于s1[i]相等的位置。
                    }
                }
                while(pos > i){
                    swap(s1[n - 1],s1[n - 2]);
                    swap(s2[pos],s2[pos - 1]);
                    pos --;
                }
            }
            if(s1[n - 1] == s2[n - 1]){
                cout << "YES" << endl;
            }
            else{
                cout << "NO" << endl;
            }
        }
    }
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> n;
        cin >> s1 >> s2;
        solve();
    }
    return 0;
}
posted @   unique_pursuit  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示