东北大学程序设计夏令营贪心

本次训练包含题目

难度 ★ :1.Robot Vacuum Cleaner 2.Reverse Maximisation 3.Cow Cars 奶牛飞车 4.Summer sell-off 5.Heaters
难度 ★★ :1.Serval and Parenthesis Sequence 2.国王游戏 3.Sum and GCD 4.Minimal string 5.Pavel and Triangles
难度 ★★★ :1.兔子与樱花 2.可做题

Problem A Serval and Parenthesis Sequence

题目链接Serval and Parenthesis Sequence
题目分类:括号匹配
题目大意:给定一个包含’?’,’(’,’)‘的字符串,要将所有的’?‘替换成’(‘或’)’,使得整个括号序列能匹配,所有前缀序列不能匹配
题目思路:还是用括号匹配的套路,’(‘看成+1,把’)'看成-1,那么显然最左边的左括号只能跟最右边的右括号匹配,不然一定有前缀和等于0。对于构造的话,还是和其他括号匹配问题一样,将左括号尽可能用在左边,这样一定是最优的
AC代码

#include <bits/stdc++.h>

using namespace std;

char s[300001];

int main() {
   
    int n;
    while (~scanf("%d", &n)) {
   
        scanf("%s", s);
        if (n % 2 == 1 || s[0] == ')' || s[n - 1] == '(') {
   
            printf(":(\n");
        } else {
   
            s[0] = '(';
            s[n - 1] = ')';
            int sum = 0, num = 0;
            for (int i = 1; i < n - 1; i ++) {
   
                if (s[i] == '(') {
   
                    num ++;
                }
            }
            if (num > (n - 2) / 2) {
   
                printf(":(\n");
            } else {
   
                for (int i = 1; i < n - 1; i ++) {
   
                    if (s[i] == '(') {
   
                        sum ++;
                    } else if (s[i] == ')') {
   
                    //如果之前遍历时,统计了右括号的数量,那么这一步就不用了
                        if (-- sum < 0) {
   
                            break;
                        }
                    } else {
   
                        if (num < (n - 2) / 2) {
   //有左括号尽量用左括号
                            s[i] = '(';
                            sum ++;
                            num ++;
                        } else {
   
                            s[i] = ')';
                            if (-- sum < 0) {
   
                                break;
                            }
                        }
                    }
                }
                if (sum >= 0) {
   
                    printf("%s\n", s);
                } else {
   
                    printf(":(\n");
                }
            }
        }
    }
    return 0;
}

在这里插入图片描述

Problem B 国王游戏

题目链接国王游戏
题目分类:高精度,严格偏序
题目大意:题目描述够清晰的了
题目思路:大臣按ai×bi排序就是所求序列。我们设所有的ai乘积为prod,考虑最下面的大臣,它所获得的奖赏为prod/(ai×bi),那么就要求ai×bi尽可能大。最后在算奖赏最多大臣的金币时,prod非常大,要用高精度。卡多组输入,我也是醉了
AC代码高精度CC一生之敌

#include <bits/stdc++.h>

using namespace std;
//高精度大数乘小数与高精度大数除小数的学习
const int MAX_N = 1000;
struct coin {
   
    int l, r;
}c[MAX_N + 1];
int prod[4010], ma[4010], temp[4010];

bool cmp(const coin &x, const coin &y) {
   
    return x.l * x.r < y.l * y.r;
}

void cheng(int t) {
   //全乘再进位
    for (int i = 1; i <= prod[0]; i ++) {
   
        prod[i] *= t;
    }
    int k;//注意这里k的使用
    for (k = 1; k <= prod[0] || prod[k]; k ++) {
   
        prod[k + 1] += prod[k] / 10;
        prod[k] %= 10;
    }
    prod[0] = k - 1;
}

void chu(int t) {
   
    int sum = 0;
    temp[0] = prod[0];
    for (int i = prod[0]; i >= 1; i --) {
   
        sum = sum * 10 + prod[i];
        temp[i] = sum / t;
        sum %= t;
    }
    while (!temp[temp[0]] && temp[0] > 0) temp[0] --;//temp[0]>0不要落了
}

void compr() {
   //先比位数,再按字典序比
    if (temp[0] > ma[0]) {
   
        for (int i = 0; i <= temp[0]; i ++) {
   
            ma[i] = temp[i];
        }
    } else if (temp[0] == ma[0]) {
   
        int len = ma[0];
        for (int i = len; i >= 1; i --) {
   
            if (temp[i] > ma[i]) {
   
                for (int j = 0; j <= temp[0]; j ++) {
   
                    ma[j] = temp[j];
                }
                break;
            } else if (temp[i] < ma[i]) {
   
                break;
            }
        }
    }
}

void prt() {
   
    if (ma[0] == 0) {
   //0要特殊考虑
        printf("0\n");
    } else {
   
        for (int i = ma[0]; i >= 1; i --) {
   
            printf("%d", ma[i]);
        }
        putchar(10);
    }
}

int main() {
   
    //freopen("in.txt", "r", stdin);
    int n;
    scanf("%d", &n);
        for (int i = 0; i <= n; i ++) {
   
            scanf("%d %d", &c[i].l, &c[i].r);
        }
        sort(c + 1, c + n + 1, cmp);
        memset(prod, 0, sizeof(prod));
        memset(ma, 0, sizeof(ma));
        prod[0] = prod[1] = 1;
        for (int i = 0; i <= n; i ++) {
   
            chu(c[i].r);
            compr();
            cheng(c[i].l);
        }
        prt();
    return 0;
}

在这里插入图片描述

Problem C Robot Vacuum Cleaner

题目链接Robot Vacuum Cleaner
题目分类:贪心,严格偏序
题目大意:将n个由只由s,h字符串拼成一个字符串,使得整体字符串sh子序列最多,之前专题有,所以难度只给了1星
题目思路:一个严格偏序的题目,先复习一下严格偏序的定义
给定一个集合S,定义S下的二元关系≺,若满足
①反自反性: ∀x ∈ S, x ≺ x均不成立
②非对称性: ∀x, y ∈ S,若x ≺ y成立,则y ≺ x不成立
③传递性: ∀x, y, z ∈ S,若x ≺ y且y ≺ z,则x ≺ z 则称二元关系≺为严格偏序
若能够证明该二元关系为严格偏序,则可通过std::sort函数得到决策的顺序
考虑相邻的两个字串p,q,若交换两个子串,则△等于p.s × q.h + p.sh + q.sh - (q.s × p.h + p.sh + q.sh) = p.s × q.h - q.s × p.h
pq要优于qp,那么p.s × q.h > q.s × p.h,变换一下就是p.s / p.h > q.s / q.h这个关系显然满足严格偏序的三条定义,故我们能够直接通过p.s / p.h的值排序获得最优序列,但是实际上为了避免单独考虑0,我们对p.s × q.h > q.s × p.h排序。
还有一个问题是如何求sh子序列,记录s(设为x),和sh(设为y)的数量,遍历整体字符串,当读到的是s时x ++当读到h时y += x,求任意长度的子序列思路都是一样的
AC代码:(s,h直接开long long要快一点)

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

struct str {
   
    string S;
    int s, h;
}c[100000];
bool cmp(const str &a, const str &b) {
   
    return 1LL * a.s * b.h > 1LL * b.s * a.h;//小心爆int
}

int main() {
   
    int n;
    while (~scanf("%d", &n)) {
   
        for (int i = 0; i < n; i ++) {
   
            cin >> c[i].S;
            c[i].h = c[i].s = 0;
            for (unsigned j = 0; j < c[i].S.size(); j ++) {
   
                if (c[i].S[j] == 's') {
   
                    c[i].s ++;
                } else {
   
                    c[i].h ++;
                }
            }
        }
        sort(c, c + n, cmp);
        long long a = 0, b = 0;//注意开long long
        for (int i = 0; i < n; i ++) {
   
            for (unsigned j = 0; j < c[i].S.size(); j ++) {
   
                if (c[i].S[j] == 's') {
   
                    a ++;
                } else {
   
                    b += a;
                }
            }
        }
        printf("%lld\n", b);
    }
    return 0;
}

在这里插入图片描述

Problem D Reverse Maximisation

题目链接Reverse Maximisation
题目分类:贪心,构造
题目大意:给定n,求1到n的数中,哪个数翻转最大,如321翻转为123
题目思路:自己最讨论写这类题了,考虑不全,老是WA,WA多了心态就炸了。我们很容易想到的,最基本的构造就是将第一个能减少1的位减1(前面位不变),然后其余位全部变为9。但这有些特殊情况,第一种就是不能减1,10…000这种类型,我们的处理就是全输9。第二种特殊情况就是10…0X99…9,把X减一输出没有意义,那我们原样输出就好了。最后一种是1,直接输出1。
AC代码:注意第一位和其余位能减一的意义不同

#include <bits/stdc++.h>

using namespace std;

char s[100001];
//没单独考虑长度为1的情况
int main() {
   
    //freopen("out.txt", "w", stdout);
    int t;
    while (~scanf("%d", &t)) {
   
        while (t --) {
   
            scanf("%s", s);
            int len = strlen(s);
            if (len == 1) {
   //1
                putchar(s[0]);
                putchar(10);
                continue;
            }
            bool flag = false;
            for (int i = 1; s[i]; i ++) {
   
                if (s[i] != '0') {
   
                    flag = true;
                    break;
                }
            }
            if (!flag && s[0] == '1') {
   //100...0类型
                for (int i =
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值