其他分享
首页 > 其他分享> > 蒟蒻的赛后分析之2021年泉州市信息学奥赛普及组五一训练赛第一场

蒟蒻的赛后分析之2021年泉州市信息学奥赛普及组五一训练赛第一场

作者:互联网

A. 抽签赢奖品
题面

小 \(y\) 小时候玩过一种游戏,一个摆摊者手里握着一个黑色的、外面看不见的扎口的袋子,里面 放了 \(n\) 张纸牌,每张纸牌上有一个数。你可以花 \(1\) 分钱到口袋里摸 \(4\) 次,每次摸出 \(1\) 张牌,记下牌上的数,再放回袋子里。如果 \(4\) 个数的和正好等于 \(m\),那么你就可以赢得一份奖品。
小 \(y\) 玩过很多回,结果一次也没赢过,郁闷了这么多年,一直不得其解。不过,小 \(y\) 自从学习 了编程后突然领悟了,那个摆局者是骗小孩钱的。这一次,小 \(y\) 穿越到那个时候,撕开口袋,取出所有纸牌,检查自己是否真的有赢的可能。
请你编写一个程序,判断当纸牌上的数分别是\(K_1,K_2,…,K_n\)时,是否存在抽取 \(4\) 次和为 \(m\) 的方 案。如果存在,输出“Yes”,否则输出“No”。
输入格式
输入数据共 \(2\) 行,第 \(1\) 行为 \(2\) 个整数,依次为 \(n\) 和 \(m\)。
第 \(2\) 行为 \(K_i\),每 \(2\) 个数之间有 \(1\) 个空格隔开。
输出格式
输出一行一个字符串“Yes”或者“No”,注意区分大小写,输出不包括引号。
对于100%的数据有:\(1 \leq n \leq 50;1 \leq m \leq 10^8;1 \leq K_i \leq 10^8\)。
输出时每行末尾的多余空格,不影响答案正确性
要求使用「文件输入输出」的方式解题,输入文件为 draw.in,输出文件为 draw.out
样例输入1
3 10
1 3 5
样例输出1
Yes
样例输入2
3 9
1 3 5
样例输出2
No

  • 题意:任意四个\(\{ k\}\)中的元素相加等于\(m\),即对于\(i, j, q, p\)存在\(k[i]+k[j]+k[q]+k[p]==m\)成立

  • 思路:看到题目\(n\)的范围\(n \leq 50\),故考虑对四个纸牌的情况进行模拟,时间复杂度\(O(n^4)\)

Code
#include <bits/stdc++.h>
using namespace std;
int n, m;
int k[55];
int main (void) {
    freopen("draw.in", "r", stdin); freopen("draw.out", "w", stdout);

    cin >> n >> m;
    for (int i(1); i <= n; ++i) {
        cin >> k[i];
        if ( 4 * k[i] == m ) {
            cout << "Yes";
            return 0;
        }
    }
    
    for (int i(1); i <= n; ++i) {
        for (int j(1); j <= n; ++j) {
            for (int q(1); q <= n; ++q) {
                for (int p(1); p <= n; ++p) {
                    if (k[i] + k[j] + k[q] + k[p] == m) {
                        cout << "Yes";
                        return 0;
                    }
                }
            }
        }
    }
    cout << "No";
}
B. 最小的字符串
题面

小 \(y\) 在玩一个电脑游戏,规则如下:
给定一个长度为 \(n\) 的字符串 \(S\),要构造一个长度为 \(n\) 的字符串 \(T\)。一开始,\(T\) 是一个空串,随后 反复进行下列任意操作:
(1) 从 \(S\) 的头部删除一个字符,加到 \(T\) 的尾部;
(2) 从 \(S\) 的尾部删除一个字符,加到 \(T\) 的尾部;
目标是要构造一个字典序尽可能小的字符串 \(T\)。
说明:字典序是指从前往后比较两个字符串大小的方法。首先比较第 \(1\) 个字符,如果不同则第 \(1\) 个字符小的字符串更小,如果相同则比较第 \(2\) 个字符,第 \(2\) 个字符小的字符串更小,…,如此继续, 来比较整个字符串的小大。
输入格式
输入数据共 \(2\) 行,第 \(1\) 行为整数 \(n\)。 第 \(2\) 行为一个字符串 \(S\),均为大写字母。
输出格式
输出一行一个字符串,为能构造出的字典序最小的字符串 \(T\)。
数据范围
对于 \(100%\) 的数据有:\(1 \leq n \leq 2000\)。
输出时每行末尾的多余空格,不影响答案正确性
要求使用「文件输入输出」的方式解题,输入文件为 minstr.in,输出文件为 minstr.out
样例输入
6
ACDBCB
样例输出
ABCBCD

  • 题意:从一个字符串的头或尾,找出一个字典序最小的字符,取出并删除,依次进行。且当首尾字典序相同时,两个方向均往内比较
  • 思路:定义首指针 \(l\) 与尾指针 \(r\),通过两个指针对字符串 \(S\) 进行取字符判断
Code
#include <bits/stdc++.h>
using namespace std;

int n;
string s;

/*判断字符串的字典序,对于字符的字典序,用正常的不等关系(大于,小于等等)就可以比较*/
bool check (int l, int r) {
	/*当首尾字符字典序相同时,往内方向比较*/
	if (s[l] == s[r]) return check(l + 1, r - 1);
	return s[l] < s[r];
}

int main (void) {
	freopen("minstr.in", "r", stdin);
	freopen("minstr.out", "w", stdout);

	cin >> n >> s;

	int l(0), r(s.size() - 1);
	while (l < r) {
		/*如果判断成功,意味着s[l] < s[r],我们输出s[l]并使l向右移动;
		 如果判断失败,意味着s[l] > s[r], 我们输出s[r]并使r向左移动*/
		if (check (l, r)) cout << s[l ++];
		else  cout << s[r --];
	}
	/*当l == r时,我们退出循环,此时s中还有一个字符,输出它可以用s[l]或s[r]*/
	cout << s[l];
}
C. 最长的绳子
题面

小 \(y\) 手里有 \(n\) 条绳子,它们的长度分别为 \(L_i\)。现在要举行跳绳比赛,为了公平,小 \(y\) 希望给 \(K\) 个参赛选手的绳子长度相同,这就需要从这 \(n\) 条绳子中切割出 \(K\) 条长度相同的绳子。但是,如果绳子太短,大家都没法跳了,所以,小 \(y\) 希望先编写一个程序,计算切割出 \(K\) 条相同长度的绳子的最长长度。
输入格式
输入数据共 \(2\) 行,第 \(1\) 行为 \(2\) 个整数,依次为 \(n\) 和 \(K\)。 第 \(2\) 行为 \(n\) 条绳子的长度\(L_i\)。
输出格式
输出一行一个实数,表示能按要求切割出的最长绳子长度。答案保留到小数点后 \(2\) 位。
数据范围
对于 \(100%\) 的数据有:
\(1 \leq n \leq 10000\);
\(1 \leq K \leq 10000\);
\(1 \leq L_i \leq 100000\)
输出时每行末尾的多余空格,不影响答案正确性
要求使用「文件输入输出」的方式解题,输入文件为 rope.in,输出文件为 rope.out
样例输入
4 11
8.02 7.43 4.57 5.39
样例输出
2.00

  • 题意:对 \(n\) 条绳子进行切割,使其成为 \(k\) 段相同长度的绳子
  • 思路:考虑到 \(1 \leq n \leq 10000\) ,暴力枚举会超时,故考虑二分答案,时间复杂度 \(O(n^2)\)
Code
#include <bits/stdc++.h>

using namespace std;

int n, K;
int len[10004];
double t;

bool check(int m) {
	int countLine(0);
	/*累计每一段可以分为多少的长度为 m 的绳子,若countLine满足绳子数量 K,则返回true*/
	for (int i(1); i <= n; ++i) {
		countLine += len[i] / m;
	}
	return countLine >= K;
}

int main (void) {
	freopen("rope.in", "r", stdin);
	freopen("rope.out", "w", stdout);

	scanf ("%d %d", &n, &K);
	for (int i(1); i <= n; ++i) {
		scanf("%lf", &t);
		/*double的精度有误差,我们对长度进行精度调整*/
		len[i] = t * 1000;
	}

	/*在答案范围内二分答案,注意到我们先前将数据放大了1000倍,故我们的二分答案的区间为[1 * 10^3, 10^5 * 10^3]*/
	int l(1e3), r(1e8), ans;
	while (l <= r) {
		int mid = (l + r) / 2;
		if (check(mid)) l = mid + 1, ans = mid;
		else r = mid - 1;
	}

	printf("%.2lf", (double)ans / 1000);

	fclose(stdin);
	fclose(stdout);
}
D. 有效的密码
题面

小 \(y\) 要给奶牛仓库安装一个新的安全系统,并且要给牛群中的每一个奶牛分配一个有效的密码。 一个有效的密码由 \(L(3 \leq L \leq 15)\) 个小写字母 \(('a'...'z')\) 组成,至少有一个元音 \(('a' , 'e' , 'i' , 'o' 或 'u' )\) 和两个辅音(除去元音以外的音节),并且是按字母表顺序出现的(例如,\('abc'\) 是有效的,而 \('bac'\) 不是) 。 给定一个期望长度 \(L\) 和 \(C(1 \leq C \leq 26)\) 个小写字母,写一个程序,输出所有的长度为 \(L\)、 能由这给定的 \(C\) 个字母组成的有效密码。密码必须按字母表顺序打印出来,一行一个。
输入格式
数据数据共 \(2\) 行。
第 \(1\) 行: 两个由空格分开的整数,\(L\) 和 \(C\)。
第 \(2\) 行: \(C\) 个由 \(1\) 个空格隔开的小写字母,密码是由这个字母集中的字母来构建的。
输出格式
若干行,每行输出一个长度为 \(L\) 个字符的密码(没有空格)。输出行必须按照字母顺序排列。你的程序只需输出前 \(25000\) 个有效密码,即使后面还存在有效密码。
数据范围
对于 \(100%\) 的数据有:\(3 \leq L \leq 15,1 \leq C \leq 26\) 。
输出时每行末尾的多余空格,不影响答案正确性
要求使用「文件输入输出」的方式解题,输入文件为 passwd.in,输出文件为 passwd.out
样例输入
4 6
a t c i s w
样例输出
acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw

  • 题意:小 \(y\) 要给奶牛分配密码,密码是至少含有一个元音字母和两个辅音字母,密码的长度为 \(L\) ,并且密码中的字母要按字母表的顺序组合
  • 思路:首先,对所给字母进行排序,然后通过 \(DFS\) 对每个字母进行选择,当所选字母数达到 \(L\) 时,进行判断这些字母的组合是否含有至少一个元音字母和两个辅音字母,满足条件即输出
Code
#include <bits/stdc++.h>

using namespace std;

int l, c, tc;
string s;
bool vis[30];

/*判断res中是否含有元音字母*/
bool check1(string res) {
    for (int i(0); i < res.size(); ++i) 
        if (res[i] == 'a' || res[i] == 'e' || res[i] == 'i' || res[i] == 'o' || res[i] == 'u') return 1;
    return 0;
}

/*cnt数res中的辅音字母,如果cnt等于2,意味着res含有至少两个辅音字母,返回true*/
bool check2(string res) {
    int cnt(0);
    for (int i(0); i < res.size(); ++i) {
        if (!(res[i] == 'a' || res[i] == 'e' || res[i] == 'i' || res[i] == 'o' || res[i] == 'u')) ++cnt;
        if (cnt == 2) return 1;
    }
    return 0;
}

/*p 上一个字母的位置, t 取了几个字母, res 当前取的字母组成的序列*/
void dfs (int p, int t, string res) {
    if (t == l) {
        if (check1(res) && check2(res)) cout << res << '\n', ++tc;
        if (tc == 25000) exit(0);
        return;
    }
    
    for (int i(p); i < c; ++i) {
        if (vis[s[i] - 'a']) continue;
        vis[s[i] - 'a'] = 1;
        dfs(i + 1, t + 1, res + s[i]);
        vis[s[i] - 'a'] = 0;
    }
}

int main (void) {
    freopen("passwd.in", "r", stdin); freopen("passwd.out", "w", stdout);
    
    cin >> l >> c;
    char t = getchar();
    for (int i(0); i < c; t = getchar()) {
        if (t >= 'a' && t <= 'z') {
            s += t;
            ++i;
        }
    }
    
    sort(s.begin(), s.end());
    dfs (0, 0, "");
}

标签:输出,int,res,字母,leq,训练赛,2021,奥赛,字符串
来源: https://www.cnblogs.com/Juro/p/14747607.html