贪心专题
- 本次训练包含题目
-
- Problem A Serval and Parenthesis Sequence
- Problem B 国王游戏
- Problem C Robot Vacuum Cleaner
- Problem D Reverse Maximisation
- Problem E 兔子与樱花
- Problem F 可做题
- Problem G Cow Cars 奶牛飞车
- Problem H Sum and GCD
- Problem I Warehouse Store
- Problem J Summer sell-off
- Problem K Minimal string
- Problem L Pavel and Triangles
- Problem M Heaters
- Problem N Greedy Florist
- 本次测试包含题目
本次训练包含题目
难度 ★ :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 =