codeforces round938 div3

本文包含了多个编程问题的解决方案,涉及排序、区间操作、动态规划、约数计算和贪心算法,展示了如何在不同场景下使用这些技术来解决问题。
摘要由CSDN通过智能技术生成

A yogurt sale

题目:

思路:分类讨论

#include <iostream>

using namespace std;

void solve() {
    int n, a, b;
    cin >> n >> a >> b;
    if(a * 2 < b) {
        cout << n * a << endl;
    } else {
        if(n % 2) cout << b * (n / 2) + a << endl;
        else cout << b * (n / 2) << endl;
    }
}

int main() {
    int t;
    cin >> t;

    while(t -- ) {
        solve();
    }
    return 0;
}

B progressive square

题目:

思路:先按列(或者行)放置生成矩阵判断是否合法,若合法再按行(列)检查,如果两个判断都通过则数据合法

#include <iostream>
#include <map>

using namespace std;

const int N = 510;

int g[N][N];

void solve() {
    map<int, int> ma;
    int n, c, d;
    cin >> n >> c >> d;

    int start = 111111111;

    for(int i = 1; i <= n; i ++ ) {
        for(int j = 1; j <= n; j ++ ) {
            int x;
            cin >> x;
            start = min(start, x);
            ma[x] ++;
        }
    }

    ma[start] --;
    g[1][1] = start;
    
    for(int i = 2; i <= n; i ++ ) {
        int a = g[1][i - 1] + d;
        if(!ma[a]) {
               cout << "NO" << endl;
               return;
           } g[1][i] = a;//处理第一行
           ma[a] --;
    }

    for(int i = 1; i <= n; i ++ ) {
        for(int j = 2; j <= n; j ++ ) {
            int a = g[j - 1][i] + c;
            if(!ma[a]) {
                cout << "NO" << endl;
                return;
            } 
            g[j][i] = a;
            ma[a] --;
        }
    }

    for(int i = 1; i <= n; i ++ ) {
        for(int j = 2; j <= n; j ++ ) {
            if(g[i][j] != g[i][j - 1] + d) {
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;
}

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

C inhabitant of the deep sea

题目:

思路:用if条件判断语句将所有情况不重不漏的分类,再分别讨论,笔者这里是一第一个战舰和最后一个战舰耐久度的高低作为分类条件

#include <iostream>

using namespace std;

const int N = 2e5 + 10;

typedef long long LL;
LL k;
int n;
long long d[N];

void solve() {
    cin >> n >> k;
    for(int i = 1; i <= n; i ++ ) cin >> d[i];

    int ans = 0;

    int i, j;

    string pre = "head";

    for(i = 1, j = n; i < j; ) {
        if(pre == "head") {
            if(d[i] <= d[j]) {
                if(k >= d[i] * 2 - 1) {
                    ans ++;
                    d[j] -= d[i] - 1;
                    k -= d[i] * 2 - 1;
                    i ++;
                    pre = "tail";
                } else {
                    cout << ans << endl;
                    return;
                }
            } else {
                if(k >= d[j] * 2) {
                    ans ++;
                    d[i] -= d[j];
                    k -= d[j] * 2;
                    j --;
                    pre = "head";
                } else {
                    cout << ans << endl;
                    return;
                }   
            }
        } else if(pre == "tail") {
            if(d[j] <= d[i]) {
                if(k >= d[j] * 2 - 1) {
                    ans ++;
                    d[i] -= d[j] - 1;
                    k -= d[j] * 2 - 1;
                    j --;
                    pre = "head";
                } else {
                    cout << ans << endl;
                    return;
                } 
            } else {
                if(k >= d[i] * 2) {
                    ans ++;
                    d[j] -= d[i];
                    k -= d[i] * 2;
                    i ++;
                    pre = "tail";
                } else {
                    cout << ans << endl;
                    return;
                }
            }
        }
    }

    if(k >= d[i]) ans ++;
    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;

    while(t -- ) {
        solve();
    }
    return 0;
}

上述方法过于繁琐,其实这个问题转化一下就变成了前面的战舰会受到 k / 2上取整次进攻,后面的战舰会受到 k / 2下取整次进攻 预处理出来前缀和与后缀和,二分找到前面进攻和后面进攻可以击落多少战舰相加即可,注意如果相加后结果大于n输出 n即可

代码

#include <iostream>
 
using namespace std;
 
const int N = 2e5 + 10;
 
typedef long long LL;
LL k;
int n;
long long d[N];
long long s1[N];
long long s2[N];
 
void solve() {
    cin >> n >> k;
    for(int i = 1; i <= n; i ++ ) cin >> d[i];
    for(int i = 1; i <= n; i ++ ) s1[i] = s1[i - 1] + d[i];
    if(k >= s1[n]) {
        cout << n << endl;
        return;
    }
    for(int i = 1; i <= n; i ++ ) s2[i] = s2[i - 1] + d[n + 1 - i];
    long long k1 = k + 1 >> 1, k2 = k >> 1;
    int l = 0, r = n;
    int ans = 0;
    while(l < r) {
        int mid = l + r + 1 >> 1;
        if(s1[mid] <= k1) l = mid;
        else r = mid - 1;
    }
    ans += l;
    l = 0, r = n;
    while(l < r) {
        int mid = l + r + 1 >> 1;
        if(s2[mid] <= k2) l = mid;
        else r = mid - 1;
    }
    ans += l;
    cout << ans << endl;
}
 
int main() {
    int t;
    cin >> t;
 
    while(t -- ) {
        solve();
    }
    return 0;
}

D inaccurate subsequence search

题目: 

 思路:该题目意为在a中有多少长度为m的子段使得这个子段中至少有k个元素与b相同,这个问题类似于,滑动窗口,只要动态的维护一段长度为m的区间,维护时判断是否满足题意,若满足则ans ++。

问题1:本身是想用队列的长度来当做判断区间是否合法的条件if(q.size() > k) ans ++ 但发现这样无法处理相同元素 例如:先用map储存b中所有的元素,如果下一个元素在map中则进队(例如b中有1, 1。由于在维护时只要是在map中的元素都会对答案产生贡献,因此窗口中不会限制有贡献元素的数量,所以窗口中可能会出现有1, 1, 1此时只能算两个1)

解决:再开一个map1维护区间中元素数量,用cnt表示区间中与b中元素相同的个数,如果在维护时发现map1中的元素数量大于本身map中元素数量,则在入队时cnt不再自增,同理,如果数量足够大,在弹出区间时cnt也不用自减

#include <iostream>
#include <unordered_map>

using namespace std;

const int N = 2e5 + 10;

int n, m, k;
int a[N], b[N];

void solve() {
    cin >> n >> m >> k;
    unordered_map<int, int> ma;//储存b数组中元素以及各种元素的数量
    unordered_map<int, int> ma1;//储存当前长度为m的窗口中属于b的元素,以及数量
    int cnt = 0;
    int ans = 0;

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

   for(int i = 1; i <= n; i ++ ) {
        int x = a[i];
        if(ma[x]) {
            ma1[x] ++;
            if(ma1[x] <= ma[x]) cnt ++;
        } 

        if(i - m > 0 && ma[a[i - m]]) {
            ma1[a[i - m]] --;
            if(ma1[a[i - m]] < ma[a[i - m]]) cnt --;
        }

        if(i - m >= 0 && cnt >= k) ans ++;
   }

    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;

    while(t -- ) {
        solve();
    }
    return 0;
}

这里代码用的unordered map容易被hack最差时间复杂度可能会到o(n^2) 

E long inversions

题目:

思路:暴力枚举可能的长度 + 贪心差分判断这个长度是否可行

#include <iostream>
#include <cstring>

using namespace std;

const int N = 5010;

char str[N];
int n;
int difference[N];
int backup[N];
int a[N];
int sum[N];

bool check(int len) {
    memcpy(backup, difference, sizeof difference);
    memset(sum, 0, sizeof sum);
    for(int i = 1; i <= n - len + 1; i ++ ) {
        sum[i] = sum[i - 1] + backup[i];
        if(!(sum[i] % 2)) {
            sum[i] += 1;
            backup[i] += 1;
            backup[i + len] -= 1;
        }
    }

    memset(sum, 0, sizeof sum);
    for(int i = 1; i <= n; i ++ ) {
        sum[i] = sum[i - 1] + backup[i];
        if(!(sum[i] % 2)) return false;
    }
    return true;   
}

void solve() {
    memset(difference, 0, sizeof difference);
    cin >> n;
    for(int i = 1; i <= n; i ++ ) {
        cin >> str[i];
        if(str[i] == '1') a[i] = 1;
        else a[i] = 0;
    }
    
    //for(int i = 1; i <= n; i ++ ) cout << a[i] << " ";
    for(int i = 1; i <= n; i ++ ) difference[i] = a[i] - a[i - 1];

    for(int k = n; k >= 1; k -- ) {
        if(check(k)) {
            cout << k << endl;
            return;
        }
    }
}

int main() {
    int t;
    cin >> t;

    while(t -- ) {
        solve();
    }
    return 0;
}

F unfair game 

题目

思路:可以发现,只有4会影响从右向左数第三位数位中1的数量,因此这个可以拉出来单独讨论,剩下的第一位数位中1的数量会被1与3影响,第二位数位中1的数量会被2与3影响因此1,2数位可以用动态规划来求dp[i][j][k]表示1,2,3卡片分别有ijk张时bob赢的最大次数,最后的答案是两部分相加

#include <iostream>

using namespace std;

const int N = 210;

int dp[N][N][N];
int p[4];

void solve() {
    for(int i = 1; i <= 4; i ++ ) cin >> p[i];
    
    dp[0][0][0] = 0;

    cout << dp[p[1]][p[2]][p[3]] + p[4] / 2 << endl;
}

int main() {
    int t;
    cin >> t;

    for(int i = 0; i < N; i ++ ) {
        for(int j = 0; j < N; j ++ ) {
            for(int k = 0; k < N; k ++ ) {
                int prev = 0;
                if(i) prev = max(prev, dp[i - 1][j][k]);
                if(j) prev = max(prev, dp[i][j - 1][k]);
                if(k) prev = max(prev, dp[i][j][k - 1]);
                dp[i][j][k] = prev;
                if(i == 0 && j == 0 && k == 0) continue;
                if(!((i + k) % 2) && !((j + k) % 2)) dp[i][j][k] ++;
            }
        }
    }

    while(t -- ) {
        solve();
    }
    return 0;
}

G gcd on a grid 

题目

思路: 类似于区间三角形线性dp,这道题每个位置的dp都可以从他正上方的位置的dp与他左方位置的dp转移过来。由于路径必须通过1,1与n,n因此答案肯定是这两点gcd的约数 因此暴力枚举这两点gcd的约数再从大到小的判断这个约数是否满足路径,如果路径上的点都是该约数的倍数即可说明该数为合法解

这里的dp[i][j]类型是bool 表示到该点的路径是否存在一条满足这条路径上的所有点都是这个数的倍数

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 110;

int g[N][N];
int n, m;
bool dp[N][N];

int gcd(int a, int b) {
    return b? gcd(b, a % b): a;
}

bool cmp(int a, int b) {
    return a > b;
}

void solve() {
    cin >> n >> m;

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

    int gc = gcd(g[1][1], g[n][m]);    

    vector<int> div;
    for(int i = 1; i <= gc / i; i ++ ) {
        if(gc % i == 0) {
            div.push_back(i);
            if(gc / i != i) 
                div.push_back(gc / i);
        }
    }

    sort(div.begin(), div.end(), cmp);
    
    dp[1][1] = true;

    for(auto d: div) {
        for(int i = 1; i <= n; i ++ ) {
            for(int j = 1; j <= m; j ++ ) {
                if(i == 1 && j != 1) {
                    dp[1][j] = dp[1][j - 1] && !(g[i][j] % d);
                } else if(j == 1 && i != 1) {
                    dp[i][1] = dp[i - 1][1] && !(g[i][j] % d);
                } else if(j != 1 && i != 1){
                    dp[i][j] = (dp[i - 1][j] && !(g[i][j] % d)) || (dp[i][j - 1] && !(g[i][j] % d));
                }
            }
        }
        if(dp[n][m]) {
            cout << d << endl; 
            return;
        }
    }
}

int main() {
    int t;
    cin >> t;

    while(t -- ) {
        solve();
    }
    return 0;
}

H the most reckless defence 

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值