Codeforces Round #753 (Div. 3) A - H

https://codeforces.com/contest/1607
趁官方题解还没出来,赶紧写一波

A

按键盘,输入一串表示键盘上的字母,保证字母只出现一次,手移动到键盘上不耗时间。给出目标字符串,问操作时间

  • 数据范围很小,暴力找即可
#include <bits/stdc++.h>

using namespace std;

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
    
    
        string s;
        cin >> s;
        int len = s.length();
        map<char, int> mp;
        for(int i=0;i<len;i++){
    
    
            mp[s[i]] = i;
        }
        cin >> s;
        len = s.length();
        int ans = 0;
        for(int i=1;i<len;i++){
    
    
            ans += abs(mp[s[i]] - mp[s[i - 1]]);
        }
        cout << ans << '\n';
    }
    return 0;
}

B

给出起点 x 0 x_0 x0和跳跃次数,规定偶数位置往左跳,奇数位置往右跳,每个时间点跳跃的距离等于时间,问 n n n次跳跃之后的位置

  • 数据范围很大,显然是找规律的问题,可以暴力打表看看
  • 推导一下,如果初始位置为 x 0 x_0 x0,那么假设是偶数,下几次的位置为 x 0 − 1 , x 0 + 1 , x 0 + 4 , x 0 x_0-1,x_0+1,x_0+4,x_0 x01,x0+1,x0+4,x0,可以看出,四个为一个周期;假如是奇数,那么下几次的位置为 x 0 + 1 , x 0 − 1 , x 0 − 4 , x 0 x_0+1,x_0-1,x_0-4,x_0 x0+1,x01,x04,x0,也是四个为一个周期,其实如果敏锐的话可以发现,这就是等差数列的一个性质,显然是 4 4 4为一个周期
  • 然后就随便写吧,我好像当时不是这么写的,写法很多
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    ll x, n;
    cin >> t;
    while(t--){
    
    
        cin >> x >> n;
        if(x & 1){
    
    
            if(n % 4 == 0) cout << x << '\n';
            else if(n % 4 == 1){
    
    
                ll a = n / 4;
                cout << x + 4 * a + 1 << '\n';
            }
            else if(n % 4 == 2) cout << x - 1 << '\n';
            else{
    
    
                ll a = n / 4;
                cout << x - 4 * (a + 1) << '\n';
            }
        }else{
    
    
            if(n % 4 == 0) cout << x << '\n';
            else if(n % 4 == 1){
    
    
                ll a = n / 4;
                cout << x - 4 * a - 1 << '\n';
            }else if(n % 4 == 2) cout << x + 1 << '\n';
            else{
    
    
                ll a = n / 4;
                cout << x + 4 * (a + 1) << '\n';
            }
        }
    }
    return 0;
}

C

给出一个数组,每次用所有的数减去最小的那一个,问直到剩下一个数这期间数组的最小值最大是多少

  • 显然,用几个数做例子, a , b , c , d a,b,c,d a,b,c,d,从小到大好排序,第一次操作之后,得到 b − a , c − a , d − a b-a,c-a,d-a ba,ca,da,大小关系不变;第二次操作之后,得到 c − b , d − c c-b,d-c cb,dc,所以求一下差分即可
#include <bits/stdc++.h>

using namespace std;

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
    
    
        int n;
        cin >> n;
        vector<int> a(n);
        for(int i=0;i<n;i++){
    
    
            cin >> a[i];
        }sort(a.begin(), a.end());
        int ans = a[0];
        for(int i=1;i<n;i++){
    
    
            ans = max(ans, a[i] - a[i - 1]);
        }
        cout << ans << '\n';
    }
    return 0;
}

D

有红色和蓝色这两种颜色,上面分别有不同的数字,蓝色的数字可以不断减 1 1 1,红色的数字可以不断加 1 1 1,现在问能不能让给定的数字通过若干次操作变成 1 − n 1-n 1n的全排列

  • 我们可以把红蓝分开看,因为蓝色只能减小,红色只能增大,那么把蓝色从小到大排序,红色从大到小排序,那么蓝色换小的,红色换大的,用双指针记录一下位置即可
#include <bits/stdc++.h>

using namespace std;

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
    
    
        int n;
        cin >> n;
        vector<int> a(n);
        string s;
        for(int i=0;i<n;i++) cin >> a[i];
        cin >> s;
        vector<int> red, blue;
        for(int i=0;i<n;i++){
    
    
            if(s[i] == 'R'){
    
    
                red.push_back(a[i]);
            }else{
    
    
                blue.push_back(a[i]);
            }
        }
        sort(blue.begin(), blue.end());
        sort(red.rbegin(), red.rend());
        int l = 1;
        int r = n;
        for(auto i : blue){
    
    
            if(i >= l){
    
    
                l += 1;
            }
        }
        for(int i : red){
    
    
            if(i <= r){
    
    
                r -= 1;
            }
        }
        if(l > r) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

E

给出一个机器人的行动方向序列,给定地图大小,问它初始位置在哪能够执行最多的指令

  • 我们可以看水平方向和竖直方向最远分别到多远的位置,如果发现这个距离差+1超过了地图大小,那么这时候后面的指令就没办法执行了,然后找个合法的位置即可
  • 注意最大和最小值的初值都是0,因为往右走不可能走出负数
#include <bits/stdc++.h>

using namespace std;

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t, n, m;
    cin >> t;
    string s;
    while(t--){
    
    
        cin >> n >> m;
        cin >> s;
        int len = s.length();
        int r = 0;
        int d = 0;
        int mxx, mxy, mnx, mny, a, b;
        mnx = mny = mxx = mxy = 0;
        for(int i=0;i<len;i++){
    
    
            a = mnx;
            b = mny;
            if(s[i] == 'L') r -= 1;
            else if(s[i] == 'R') r += 1;
            else if(s[i] == 'D') d += 1;
            else d -= 1;
            mxx = max(mxx, d);
            mxy = max(mxy, r);
            mnx = min(mnx, d);
            mny = min(mny, r);
            // cout << mxx << ' ' << mxy << ' ' << mnx << ' ' << mny << '\n';
            if(mxx - mnx + 1 > n || mxy - mny + 1 > m){
    
    
                break;
            }
            if(i == len - 1){
    
    
                a = mnx;
                b = mny;
            }
        }
        cout << 1 - a << ' ' << 1 - b << '\n';
    }
    return 0;
}

F

现在还是机器人走路,但是问题变成了给定每个格子的移动方向,问机器人从哪出发能够执行最多的指令,并要求统计执行指令的次数,机器人如果走到一个走过的格子或者走出地图就停止

  • 如果直接暴力显然会超时,考虑一下,因为每个点的移动方向都是固定的,那么我们如果能把 d f s dfs dfs的过程变成 O ( n m ) O(nm) O(nm)的就好了,怎么处理呢?显然如果出现环,那么环上的每个位置能够执行指令的次数就是环长,如果没有环,走出了地图,那么执行指令的次数就是递归深度
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 2e3 + 5;
char mp[MAXN][MAXN];
int dis[MAXN][MAXN];
int vis[MAXN][MAXN];
pair<int, int> PII[MAXN * MAXN];
int cnt, t, n, m;
int Dfs(int x, int y){
    
    
    if(x < 1 || y < 1 || x > n || y > m) return 0;
    PII[cnt++] = make_pair(x, y);
    if(vis[x][y]) return dis[x][y];
    vis[x][y] = 1;
    if(mp[x][y] == 'U') return dis[x][y] = Dfs(x - 1, y) + 1;
    else if(mp[x][y] == 'D') return dis[x][y] = Dfs(x + 1, y) + 1;
    else if(mp[x][y] == 'L') return dis[x][y] = Dfs(x, y - 1) + 1;
    else return dis[x][y] = Dfs(x, y + 1) + 1;
    return dis[x][y];
}
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while(t--){
    
    
        cin >> n >> m;
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                cin >> mp[i][j];
            }
        }
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                if(!dis[i][j]){
    
    
                    cnt = 0;
                    Dfs(i, j);
                    auto q = PII[cnt - 1];
                    for(int k=0;k<cnt-1;k++){
    
    
                        if(PII[k] == q){
    
    
                            for(int s=k+1;s<cnt;s++){
    
    
                                dis[PII[s].first][PII[s].second] = cnt - k - 1;
                            }
                        }
                    }
                }
            }
        }
        int mx = -1e8;
        int a, b;
        a = b = -1;
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                if(mx < dis[i][j]){
    
    
                    mx = dis[i][j];
                    a = i;
                    b = j;
                }
                dis[i][j] = vis[i][j] = 0;
            }
        }
        cout << a << ' ' << b << ' ' << mx << '\n';
    }
    return 0;
}
  • 环长的判断画画图就明白了
  • 这里还有一个MLE的问题,我用vector就会MLE,这时候抛弃一切骚操作,使用静态数组就没事了

写着写着突然官方题解出来了。。。猝不及防

G

n n n道菜,每道菜有鱼和肉的质量分别给出分别为 a [ i ] , b [ i ] a[i],b[i] a[i],b[i],给出一个数 m m m表示要从每道菜里面吃掉多少克,可以吃鱼也可以吃肉,最终要求每道菜鱼的质量和与肉的质量和的差的绝对值尽可能地小,问方案

  • 我们可以采用一种贪心策略,假设我们全吃鱼,然后看看需要吃多少肉,这样就进行好了一轮
  • 接下来我们会得到一个新的 s u m a , s u m b suma,sumb suma,sumb我们现在想用 b b b来调节这二者之间的差值,为什么要使用这种方法?如果我们直接贪心,是双变量,不好控制,而现在变成了一个变量,如果我们发现 s u m a < s u m b suma<sumb suma<sumb,那我们就可以多吃点 b b b,少吃点 a a a,也就是给 a a a补一部分,这部分应该是多少呢?显然这部分不能超过 a a a的吃掉的量,且也不能超过 s u m b − s u m a 2 \frac{sumb-suma}{2} 2sumbsuma,因为我们要让它们接近,更不能超过 b b b的剩余量,所以应该是 m i n { s u m b − s u m a 2 , a n s _ a [ i ] , b [ i ] − a n s _ b [ i ] } min\{\frac{sumb-suma}{2},ans\_a[i],b[i]-ans\_b[i]\} min{ 2sumbsuma,ans_a[i],b[i]ans_b[i]}
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t, n;
    ll m;
    cin >> t;
    while(t--){
    
    
        cin >> n >> m;
        vector<ll> a(n), b(n), ans_a(n), ans_b(n);
        ll suma, sumb;
        suma = sumb = 0;
        for(int i=0;i<n;i++){
    
    
            cin >> a[i] >> b[i];
            suma += a[i];
            sumb += b[i];
            ans_a[i] = min(m, a[i]);
            ans_b[i] = m - ans_a[i];
            suma -= ans_a[i];
            sumb -= ans_b[i];
        }
        for(int i=0;i<n;i++){
    
    
            if(suma < sumb){
    
    
                ll g = min({
    
    (sumb - suma) / 2, ans_a[i], b[i] - ans_b[i]});
                ans_a[i] -= g;
                ans_b[i] += g;
                suma += g;
                sumb -= g;
            }else if(suma > sumb) {
    
    
                ll g = min({
    
    (suma - sumb) / 2, ans_b[i], a[i] - ans_a[i]});
                ans_a[i] += g;
                ans_b[i] -= g;
                suma -= g;
                sumb += g;
            }
        }
        cout << abs(suma - sumb) << '\n';
        for(int i=0;i<n;i++){
    
    
            cout << ans_a[i] << ' ' << ans_b[i] << '\n';
        }

    }
    return 0;
}

H

还是 n n n道菜,现在每道菜都有一个需要吃掉的量,现在说如果菜之间的鱼质量和肉质量分别相等,那么就说这是同一道菜,现在想让菜的种类数尽可能地少,问吃菜的方案

  • 这道题的破题点在于假设两道菜有可能一样,那么它们的 a + b − m a+b-m a+bm一定是一样的,如果能想到这,这道题就做了一半
  • 那么我们可以开个桶,把不同的 a + b − m a+b-m a+bm放在不同的桶里面,这样我们只需要在每个桶里面看能构成多少种菜就好了,因为不同桶的元素之间不可能构成一种菜;同种桶里面也可能构成多道菜,例如 5 , 8 , 2 5,8,2 5,8,2 8 , 5 , 2 8,5,2 8,5,2,只能是两道菜
  • 接下来我们怎么判断一个桶里面的元素是不是能够构成一道菜呢?容易想到,我们贪心的将桶里面的元素按照 a a a从小到大的顺序排序,我们再尽可能地多拿 b b b,实在不够再拿 a a a,初始时候肯定是有一道菜,设这时候 a a a的质量为 r e s res res,那么接下来我也要把 a a a的质量拿成 r e s res res,如果达不到,那么就得多一道菜,这中间的差值是 a − r e s a-res ares,如果 a − r e s > m a-res>m ares>m,说明多一道菜,因为我们把 a a a从小到大排序了,所以接下来就更拿不到了,这时候要更新 a a a的剩余量,统计答案;否则说明能达到,统计答案即可
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t, n;
    cin >> t;
    while(t--){
    
    
        cin >> n;
        vector<ll> a(n), b(n), m(n);
        map<ll, vector<ll> > mp;
        for(int i=0;i<n;i++){
    
    
            cin >> a[i] >> b[i] >> m[i];
            mp[a[i] + b[i] - m[i]].push_back(i);
        }
        vector<ll> ans(n);
        ll k = 0;
        for(auto i : mp){
    
    
            auto u = i.second;
            sort(u.begin(), u.end(), [&](int x, int y){
    
    
                return a[x] < a[y];
            });
            ll res = -1e16;
            for(auto j : u){
    
    
                if(a[j] - res > m[j]){
    
    
                    k += 1;
                    ans[j] = max(0ll, m[j] - b[j]);
                    res = a[j] - ans[j];
                }else{
    
    
                    ans[j] = a[j] - res;
                }
            }
        }
        cout << k << '\n';
        for(int i=0;i<n;i++){
    
    
            cout << ans[i] << ' ' << m[i] - ans[i] << '\n';
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/121153560