1.小美的外卖订单
小美正在设计美团外卖的定价信息。已知外卖定价的规则如下:
- 每道菜有折扣价和原价。折扣价不能超过原价。
- 订单有满x元减y元的优惠。当购买的菜的价格总和不小于x元时,总价格可以减y元。“减”的价格不能超过“满”的价格。
- 满减优惠和折扣价是互斥的,当且仅当每个菜都选择了原价才可以触发满减。
- 系统会自动为客户计算最低价格的方案。
在设计定价时,原价、折扣价和满减的价格都必须是正实数。如果设计的定价发生问题,则会提示数据错误。
请使用等价划分法设计测试用例,来测试该系统的功能。
输入描述
第一行输入一个正整数n,代表菜的总数。
接下来的n行,每行输入两个实数a_i和b_i,代表每道菜的原价是a_i,折扣价是b_i。
最后一行输入两个实数x和y,代表满x元可以减y元。
数据中所有实数的绝对值不超过1000。
输出描述
如果数据有误,则输出一行字符串"error"。
否则输出一个小数,小数点后保留2位即可。该小数代表顾客购买了全部菜各一份时,订单的总价格。
示例
输入例子:
2
10 5.5
10 6.5
15 3
输出例子:
12.00
例子说明:
虽然触发了满15元减3元,但使用折扣只需要花12元,低于使用满减的价格(20-3=17),因此最终系统会为客户推荐折扣价。
输入例子:
2
10 5.5
10 6.5
20 10
输出例子:
10.00
例子说明:
触发满20元减10元即可。满减价优于折扣价。
输入例子:
2
10 10.25
10 3.5
20 4.5
输出例子:
error
例子说明:
折扣价高于原价,数据错误。
解题思路
刚看题的时候被吓住了,还以为可以又原价又折扣,一看是互斥的就变成了easy题了,但是特别恶心测试用例搞事情,要double才行,否则个10w个数据就放不下了,然后就是要注意非法输入错误处理。
代码
#include <iostream>
#include <vector>
#include<iomanip>
using namespace std;
int main() {
int n; // n 道菜
cin >> n;
vector<double> prices(n);
vector<double> discountprices(n);
double sumo = 0;
double sumdis = 0;
for (int i = 0; i < n; ++i) {
double discount;
cin >> prices[i] >> discountprices[i];
sumo += prices[i];
sumdis += discountprices[i];
if(discountprices[i] <= 0 || prices[i] <= 0 || discountprices[i] > prices[i]){
cout << "error" << endl;
return 0;
}
}
double enough, sub;
cin >> enough >> sub;
if (sub <= 0 | enough <= 0 || sub > enough) {
cout << "error" << endl;
return 0;
}
if (sumo > enough) {
sumo -= sub;
}
cout << fixed << setprecision(2) << min(sumo, sumdis) << endl;
}
2.小美的字符串匹配度
小美有两个长度为n只包含小写字母的字符串s和t,小美定义“两个字符串的匹配度”为i∈[1,n]中s i =t i 的数量,例如"abacd"和"aabdd"的匹配度就是2。现在你可以进行最多一次以下操作:对于字符串t,选择两个索引i,j(1≤i<j≤n),交换t i 和t j 。小美想知道,s和t的最大字符串匹配度是多少?
输入描述
第一行输入一个整数n
第二行输入一个长度为n的字符串s。
第三行输入一个长度为n的字符串t。
输出描述
输出一个整数,s和t的最大匹配度。
示例
输入例子:
5
ababc
babac
输出例子:
3
解题思路
先找一遍相等的,把不等的位置放到一个vector中,再到这个vector中遍历,看是否能交换到相等,或者交换后只让一个位置可以等也行。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
string str1, str2;
cin >> str1;
cin >> str2;
// 能交换就能加2,不能就不加
bool change2 = false;
bool change1 = false;
int cnt = 0;
vector<int> otherpos;
for (int i = 0; i < n; ++i) {
if (str1[i] == str2[i])
cnt++;
else {
otherpos.push_back(i);
}
}
for (int i : otherpos) {
for (int j : otherpos) {
if (i == j)
continue;
if (str1[i] == str2[j] && str1[j] == str2[i]) {
change2 = true;
break;
}else if(str1[i] == str2[j]){
change1 = true;
}
}
}
if (change2) {
cnt += 2;
}
else if (change1) {
cnt += 1;
}
cout << cnt << endl;
}
3.小美的树上染色
小美拿到了一棵树,每个节点有一个权值。初始每个节点都是白色。小美有若干次操作,每次操作可以选择两个相邻的节点,如果它们都是白色且权值的乘积是完全平方数,小美就可以把这两个节点同时染红。小美想知道,自己最多可以染红多少个节点?
输入描述
第一行输入一个正整数n,代表节点的数量。
第二行输入n个正整数a_i,代表每个节点的权值。
接下来的n-1行,每行输入两个正整数u,v,代表节点u和节点v有一条边连接。
输出描述
输出一个整数,表示最多可以染红的节点数量。
示例
输入例子:
3
3 3 12
1 2
2 3
输出例子:
2
例子说明:
可以染红第二个和第三个节点。
请注意,此时不能再染红第一个和第二个节点,因为第二个节点已经被染红。
因此,最多染红 2 个节点。
解题思路
想着构造出树出来,然后一个回溯,没写出来,求教。看大佬用拓扑排序做的,这段时间好好看一看。
代码
#define WHITE 1
#define RED 0
typedef int color;
struct treenode {
color c; // 颜色
treenode* parent; // 父结点
vector<treenode*> children; // 子结点
int value; // 权值
treenode(int v) :value(v) { c = WHITE; };
};
bool isSqrt(int value) {
int sq = sqrt(value);
return sq == (value / sq);
}
int ret = -1;
void dfs(treenode* node, int& value) {
// 怎么做啊怎么做
}
int main() {
int n; // n个结点
cin >> n;
vector<treenode*> nodes(n+1);
vector<int> values(n+1);
for (int i = 1; i <= n; ++i) {
cin >> values[i];
treenode* node = new treenode(values[i]);
nodes[i] = node;
}
nodes[1]->parent = nodes[0];
nodes[0]->c = RED;
for (int i = 1; i < n; ++i) {
int p, c;
cin >> p >> c;
if (p > c)swap(p, c);
nodes[c]->parent = nodes[p];
nodes[p]->children.push_back(nodes[c]);
}
int value = 0;
dfs(nodes[1], value);
cout << ret << endl;
return 0;
}
4.小美的排列询问
小美拿到了一个排列。她想知道在这个排列中,x和y是否是相邻的。你能帮帮她吗?排列是指一个长度为n的数组,其中 1 到n每个元素恰好出现一次。
输入描述
第一行输入一个正整数n,代表排列的长度。
第二行输入n个正整数a_i,代表排列的元素。
第三行输入两个正整数x和y,用空格隔开。
输出描述
如果x和y在排列中相邻,则输出"Yes"。否则输出"No"。
示例
输入例子:
4
1 4 2 3
2 4
输出例子:
Yes
输入例子:
5
3 4 5 1 2
3 2
输出例子:
No
解题思路
遍历一遍找左右。
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
bool ret = false;
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; ++i) cin >> nums[i];
int num1, num2;
cin >> num1 >> num2;
for (int i = 0; i < n; ++i) {
if (nums[i] == num1) {
if ( i>0 && nums[i-1] == num2) {
ret = true;
}
if (i < n - 1 && nums[i + 1] == num2) {
ret = true;
}
}
}
if (ret)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
5.小美的排列构造
小美定义一个数组
a的权值计算如下:
首先将a的每一对相邻两项求和,得到一个b数组。那么b数组的最大值减最小值即为a数组的权值。例如,若[2,1,3]a=[2,1,3],那么[3,4]b=[3,4],b数组的极差是1。因此a数组的权值为1。现在小美希望你能构造一个长度为n的排列,满足权值尽可能小。你能帮帮她吗?排列是指一个长度为n的数组,其中 1 到n每个元素恰好出现一次。
输入描述
一个正整数n,代表排列的长度。
输出描述
一个合法的排列。如果有多解输出任意即可。
示例
输入例子:
3
输出例子:
2 1 3
例子说明:
这个数组的权值为 1。输出[2,3,1]等排列也是合法的。
解题思路
妖法,把中间值(奇数)中间值后一个(偶数)放第一个,后面依次放这个值 减 位置值 / 2 向上取整。这样相邻的两数加和差就为 1 了。
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> ret(n);
int tmp = n / 2 + 1;
int num;
ret[0] = tmp;
for (int i = 1; i < n; ++i) {
if (i % 2 == 0) {
num = i / 2;
ret[i] = tmp + num;
}
else {
num = i / 2 + 1;
ret[i] = tmp - num;
}
}
for (auto i : ret) cout << i << " ";
}
6.小美走公路
有一个环形的公路,上面共有n站,现在给定了顺时针第i站到第i+1站之间的距离(特殊的,也给出了第n站到第 1 站的距离)。小美想沿着公路第x站走到第y站,她想知道最短的距离是多少?
输入描述
第一行输入一个正整数n,代表站的数量。
第二行输入n个正整数a_i,前n-1个数代表顺时针沿着公路走,i站到第i+1站之间的距离;最后一个正整数代表顺时针沿着公路走,第n站到第 1 站的距离。·
第三行输入两个正整数x和y,代表小美的出发地和目的地。
输出描述
一个正整数,代表小美走的最短距离。
示例
输入例子:
3
1 2 2
2 3
输出例子:
2
输入例子:
3
1 2 2
1 3
输出例子:
2
解题思路
左边右边算了比较大小。注意要long。
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<long> nums(n);
for (int i = 0; i < n; ++i)cin >> nums[i];
int a, b;
cin >> a >> b;
a -= 1; b -= 1;
if (a > b)swap(a, b);
long right = 0;
for (int i = a; i < b; ++i) {
right += nums[i];
}
long left = 0;
for (int i = b; i < n; ++i) {
left += nums[i];
}
for (int i = 0; i < a; ++i) {
left += nums[i];
}
long ret = min(left, right);
cout << ret << endl;
}
7.小美的好矩阵
小美定义一个矩阵是好矩阵,当且仅当该矩阵满足:
- 矩阵仅由’A’、‘B’、'C’三种字符组成。且三种字符都出现过。
- 矩阵相邻的字符都不相等。
现在给定一个n∗m的矩阵,小美想知道有多少个3*3的子矩阵是好矩阵,你能帮帮她吗?
输入描述
第一行输入两个整数n,m,代表矩阵的行数和列数。
接下来的n行,每行输入一个仅包含大写字母的长度为m的字符串。
输出描述
输出一个整数表示答案。
示例
输入例子:
4 4
DABC
ABAB
BABA
BBAB
输出例子:
1
例子说明:
有4个3*3的子矩阵。
左上角的子矩阵出现了’D’,因此不合法。
右上角的是好矩阵。
左下角的存在两个相邻的字母相同,因此不合法。
右下角的子矩阵里没有’C’,因此不合法。
解题思路
话不多说,直接暴力。
代码
#include <iostream>
#include <vector>
using namespace std;
bool isGood(vector<vector<char>>& matrix, int n, int m) { // 左上角起始处
int cntA = 0, cntB = 0, cntC = 0;
for (int i = n; i < n + 3; ++i) {
for (int j = m; j < m + 3; ++j) {
if (matrix[i][j] == 'A')
cntA++;
else if (matrix[i][j] == 'B')
cntB++;
else if (matrix[i][j] == 'C')
cntC++;
else
return false;
// 下右
if (i + 1 < n + 3 && matrix[i][j] == matrix[i + 1][j] || j + 1 < m + 3 && matrix[i][j] == matrix[i][j+1])
return false;
// 上左
if (i > n && matrix[i][j] == matrix[i - 1][j] || j > m && matrix[i][j] == matrix[i][j - 1])
return false;
}
}
if (cntA == 0 || cntB ==0 || cntC == 0)
return false;
return true;
}
int main() {
int n, m; // n 行 m 列
cin >> n >> m;
vector<vector<char>> matrix(n, vector<char>(m));
for (int i = 0; i < n; ++i) {
string input;
cin >> input;
for (int j = 0; j < m; ++j) {
matrix[i][j] = input[j];
}
}
int ret = 0;
for (int i = 0; i < n - 2; ++i) {
for (int j = 0; j < m - 2; ++j) {
if (isGood(matrix, i, j))
ret++;
}
}
cout << ret << endl;
}
8.小美的蛋糕切割
小美有一个矩形的蛋糕,共分成了 n 行 m 列,共 n×m 个区域,每个区域是一个小正方形,已知蛋糕每个区域都有一个美味度。她想切一刀把蛋糕切成两部分,自己吃一部分,小团吃另一部分。小美希望两个人吃的部分的美味度之和尽可能接近,请你输出∣∣s1−s2∣|的最小值。(其中s1代表小美吃的美味度,s2 代表小团吃的美味度)。请务必保证,切下来的区域都是完整的,即不能把某个小正方形切成两个小区域。
输入描述
第一行输出两个正整数 n 和 m ,代表蛋糕区域的行数和列数。
接下来的 n 行,每行输入 m 个正整数 a_{ij} ,用来表示每个区域的美味度。
输出描述
一个整数,代表 |s_1-s_2| 的最小值。
示例
输入例子:
2 3
1 1 4
5 1 4
输出例子:
0
例子说明:
把蛋糕像这样切开:
1 1 | 4
5 1 | 4
左边蛋糕美味度之和是8
右边蛋糕美味度之和是8
所以答案是0。
解题思路
横着切,竖着切。比较横着切竖着切的最小值。注意点:只有一块,返回;看到美团的题目不要选int 都用long。
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m; // n 行 m 列
cin >> n >> m;
vector<vector<long>> cakes(n, vector<long>(m));
long sum = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> cakes[i][j];
sum += cakes[i][j];
}
}
if(m==1 && n==1){
cout << cakes[0][0] <<endl;
return 0;
}
// 横着切
long minh = 1e9;
for (int k = 1; k < n; ++k) {// 从 1 开始,算 1 及其以下
long down = 0;
for (int i = k; i < n; ++i) {
for (int j = 0; j < m; ++j) {
down += cakes[i][j];
}
}
long up = sum - down;
minh = min(minh, abs(up - down));
}
// 竖着切
long minv = 1e9;
for (int k = 1; k < m; ++k) {// 从 1 开始,算 1 及其右边
long right = 0;
for (int i = 0; i < n; ++i) {
for (int j = k; j < m; ++j) {
right += cakes[i][j];
}
}
long left = sum - right;
minv = min(minv, abs(left - right));
}
cout << min(minv, minh) << endl;
}
9.小美的字符串变换
小美拿到了一个长度为n的字符串,她希望将字符串从左到右平铺成一个矩阵(先平铺第一行,然后是第二行,以此类推,矩阵有x行y列,必须保证x∗y=n,即每y个字符换行,共x行)。该矩阵的权值定义为这个矩阵的连通块数量。小美希望最终矩阵的权值尽可能小,你能帮小美求出这个最小权值吗?
注:我们定义,上下左右四个方向相邻的相同字符是连通的。
输入描述
第一行输入一个正整数n,代表字符串的长度。
第二行输入一个长度为n的、仅由小写字母组成的字符串。
输出描述
输出一个整数表示最小权值。
示例
输入例子:
9
aababbabb
输出例子:
2
例子说明:
平铺为3*3的矩阵:
aab
abb
abb
共有2个连通块,4个a和5个b。
解题思路
计算当这个矩阵是 x 行 y 列 的矩阵时,最大连通域数,最大连通域数 用一个 bool,先把能访问的都置为true 再要访问false的就cnt++。
代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void backtrace(string str, vector<bool>& flags, int numrows, int numcols, int row, int col, char ch){
int offset = row * numcols + col;
if(row < 0 || row >=numrows || col < 0 || col >= numcols || flags[offset] || ch != str[offset]) // 越界 被访问过 不连通
return;
flags[offset] = true;
backtrace(str, flags, numrows, numcols, row + 1, col, ch);
backtrace(str, flags, numrows, numcols, row - 1, col, ch);
backtrace(str, flags, numrows, numcols, row, col+1, ch);
backtrace(str, flags, numrows, numcols, row, col-1, ch);
}
int main() {
int n;
cin >> n;
string input;
cin >> input;
int ret = 1e9;
for(int x=1; x<n; ++x){
// x 行
if(n%x) // 除不尽
continue;
// y 列
int y = n / x;
vector<bool> flags(n, false);
int cnt = 0;
for(int row = 0; row< x; ++row){
for(int col=0; col<y; ++col){
int offset = row*y+col;
if(!flags[offset]){
++cnt;
backtrace(input, flags, x, y, row, col, input[offset]);
}
}
}
ret = min(ret, cnt); // x 行 y 列 的最大连通域数
}
cout << ret <<endl;
}
总结
在没有测试用例的情况下
一、数据类型尽可能用大的,int 用 long 甚至long long float 用double。
二、错误数据的判断,要对所有输入用例是否符合规则进行判断。
三、树的题目要用拓扑,需要再学习一下,其他的AC了。