Codeforces Round #809 (Div. 2) A-D1

Codeforces Round #809 (Div. 2)

https://codeforces.com/contest/1706

A. Another String Minimization Problem

题意

现有一个长度为n的序列a, (\(1\leq a_i\le m\))和一个长度为m的全'B'字符串。每次操作可以把串中位置为 \(a_i\)\(m+1-a_i\) 的字符替换成 'A'。问构成最小的字典序的串长啥样

分析

因为字典序中'A'小于'B',所以如果当前位置能替换成A就一定要替换,具体可以拿一个数组来维护该位置是否可替换

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 55;
int n, m;
int a[N];

void solve () {
    memset (a, 0, sizeof a);
    cin >> n >> m;
    set<int> s;
    for (int i = 0; i < n; i ++) {
        int x;
        cin >> x;
        a[x] ++, a[m+1-x] ++;
    }
    for (int i = 1; i <= m; i ++) {
        if (a[i] > 0)    cout << 'A', a[i] --, a[m+1-i] --;
        else    cout << 'B';
    }
    cout << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

//A尽可能在前面
//能变的都变

B. Making Towers

题意

给定一个序列a,\(a_i\) 代表第 \(i\) 个方块的颜色。现在可以把这些方块堆积起来,堆积的规则是:每次可以往左/右/正上方堆。问相同颜色的能堆在一起的最高高度是多少

分析

观察例题的图,我们可以发现一条性质

相同颜色的方块能够堆在一起的条件是当且仅当这两个方块中间隔着偶数个方块,因为相邻的块要么直接堆在一起,要么往旁边堆,再"拐"回来,所以序号的差值必须要是偶数

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 5;
int n, m;
//int a[N];

void solve () {
    cin >> n;
    vector <int> v[n+1]; //记录下标
    for (int i = 0; i < n; i ++) {
        int x;
        cin >> x;
        if (!v[x].empty ()) {
            int lst = v[x].back();
            if ((i - lst) % 2 == 1)
                v[x].push_back (i);
        }
        else    v[x].push_back (i);
    }

    if (n == 1) {
        cout << 1 << endl;
        return ;
    }

    for (int i = 1; i <= n; i ++)   
        cout << v[i].size() << ' ';
    
    cout << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

//A尽可能在前面
//能变的都变

//y不减,都是往上走
//左/右/上
//相同颜色之间的间隔只有是偶数的时候才能叠在一起

C. Qpwoeirut And The City

题意

给一个序列a,对于任意 \(1<i<n\),当且仅当 \(a_i\) 满足 \(a_{i-1}<a_i\)\(a_{i+1}<a_i\) 时,称 \(a_i\) 是 cool 的
现在可以进行的操作是,对任意 \(a_i\) 都可以增加其高度,使之变成一个 cool 的 \(a_i\)
问 在 cool 的数量最大的情况下,所需增加的总高度最小为多少

分析

考虑数量最大为多少,不难发现,当n为奇数的时候,方案是固定的,即选取 位于 2,4,6,...,n-1 上的数把他变为cool,才能使得cool 的数量最大

当n为偶数的时候就要考虑一下选取的可能性了,即 转化为一个子问题:

对于一个长度为len(len为偶数)的序列从中选len/2个数求和,且这些数不能相邻,那么和最小为多少

做法一

贪心,枚举每一种特殊情况
先上个图:


(注:途中序列是原序列的2-n处)

对于这样的一段策略,我们可能的选择就是:

  1. 每隔一个选一个,那么有两种可能,见情况1和2
  2. 某一个位置隔两个选一个,剩余的位置隔一个选一个。那么在这种情况下,要注意隔两个选取的特殊点有且仅有一个(因为多了就没法保证能选上len/2个数了),并且这一段的起始位置的下标必须是偶数(也是为了保证能选上尽可能多的数)

那么就可以枚举每一个特殊段(隔两个)出现的位置。
具体怎么做呢,就是预处理两个前缀和数组,具体来说是一个前缀和,一个后缀和,然后就可以枚举每一个分割段了,详见代码。
(我称之为"错位前缀和")

做法二

dp
想法来自Hytidel
dp[i]表示楼i是最后一个cool的最小代价,偶数的答案就是dp[n-1]和dp[n-2]取min

太妙了orz
他的代码:https://codeforces.com/contest/1706/submission/164846285

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int n, m;
int a[N], f[N][2]; //当前i 是否为最值

void solve () {
    cin >> n;

    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
    }

    if (n & 1) {  
        int ans = 0;  
        for (int i = 2; i < n; i += 2) {
            int maxn = max (a[i-1], a[i+1]) + 1;
            if (a[i] >= maxn)   continue;
            ans += maxn - a[i];  
        }
        cout << ans << endl;    
    }

    else {
        int ans1 = 0, ans2 = 0;
        vector <int> cost(n+1);

        for (int i = 2; i < n; i ++) {
            int maxn = max (a[i-1], a[i+1]) + 1, x;
            if (a[i] >= maxn)   x = 0;
            else    x = maxn - a[i];  
            cost[i] = x;
            //cout << "i=" << i << ", cost=" << cost[i] << endl;
        }      
        //贪心,步长为1或2
        //为2的只会有0个或一个,只需枚举其位置即可
        int ans = 1e9 + 10;

        //0个
        // int dx = 0;
        // for (int i = 2; i < n; i += 2) 
        //     dx += cost[i];
        // ans = min (ans, dx), dx = 0;

        // for (int i = 3; i < n; i += 2) 
        //     dx += cost[i];
        // ans = min (ans, dx), dx = 0;

        //1个
        vector <int> sum1(n+1), sum2(n+1);
        sum1[2] = cost[2], sum2[n-1] = cost[n-1];
        for (int i = 4; i < n; i += 2)    sum1[i] = sum1[i-2] + cost[i];
        for (int i = n-3; i > 2; i -= 2)    sum2[i] = sum2[i+2] + cost[i];
        ans = min (sum1[n-2], sum2[3]);

        // for (int i = 3; i < n; i ++) {
        //     cout << i-1 << ' ' << sum1[i-1] << ", " << i << ' ' << sum2[i] << endl;
        // }

        for (int i = 2; i < n-3; i ++) {
            if (i % 2 == 0)
                ans = min (ans, sum1[i] + sum2[i+3]);
            //cout << sum1[i] + sum2[i+3] << endl;
        }
        cout << ans << endl;
    }    
}

signed main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

//cool:比左右两边的都高
//在cool最多的前提下,要增高的总高度最小
//dp
//当前作为最值的话相邻的肯定不能作为最值
//可以先贪一下两种情况

//数量是要有保证的,必须为(n-1)/2

//注:差两个的起点只会出现在下标为偶数的情况里
//优化:错位前缀和

D1. Chopping Carrots (Easy Version)

题意

给出一个不降的序列a,要求构造出序列p,使得\(a_i/p_i\)的最大值 减去 最小值最小

分析

枚举枚举pi最小值,pi一定小于最小的ai
算是比较暴力的做法了

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 3005;
int a[N], n, k;

void solve () {
    cin >> n >> k;
    int ans = 1e9, minn = 1e9;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        //minn = min (minn, a[i]);
    }   
    if (k >= a[n])  ans = 0;
    else if (k == 1)    ans = a[n] - a[1];
    else {
        //枚举pi最小值,pi一定小于最小的ai
        for (int i = 1; i <= a[1]; i ++) {
            int dx = 0;
            for (int j = 1; j <= n; j ++) {
                int x = min (a[j]/i, k);
                dx = max (dx, a[j]/x);
            }
            ans = min (ans, dx - i); //差值
        }
    }
    cout << ans << endl;
}

int main () {
    ios::sync_with_stdio(0);cin.tie(0);
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}


//构造n个pi,范围在1-k,使得max(ai/pi)-min(ai/pi) 最小
//k=1     -> a[n]-a[1]
//k>=a[n] -> 0

//一般情况:根据最小的(a[n]/k)来构造

D2. Chopping Carrots (Hard Version)

E. Qpwoeirut and Vertices

看了题解说是个kruscal重构树,那我还是会回来补一补的
只是什么时候就不太清楚了
https://blog.csdn.net/m0_61735576/article/details/124804973

posted @ 2022-07-19 17:27  Sakana~  阅读(228)  评论(0编辑  收藏  举报