我是真的菜。
第一题——画家小Q
题意
画家小Q又开始他的艺术创作。小Q拿出了一块有N×M像素格的画板,画板初始状态是空白的,用“X”表示。
小Q有他独特的绘画技巧,每次小Q会选择一条斜线:
如果斜线的方向形如‘/’即斜率为1,小Q会选择这条斜线中的一段格子,都涂画为蓝色,用‘B’表示;
如果对角线的方向形如‘\’,即斜率为-1,小Q会选择这条斜线中的一段格子,都涂画为黄色,用‘Y’表示。
如果一个格子既被蓝色涂画过又被黄色涂画过,那么这个格子就会变成绿色,用‘G’表示。
小Q已经有想画出的作品的样子,请你帮他计算一下他最少需要多少次操作完成这幅画。输入描述:
第一行包含两个正整数N和M(1<=N,M<=50),表示画板的长宽。
接下来的N行包含N个长度为M的字符串,其中包含‘B’,‘Y’,‘G’,‘X’,分别表示蓝色,黄色,绿色,空白。整个表示小Q要完成的作品。输出描述:
一个正整数,表示小Q最少需要多少次操作完成绘画。
输入
4 4
YXXB
XYGX
XBYX
BXXY
输出
3
思路
现在已经有一幅成型的画了,我们要思考的是最少操作怎么还原这幅画,所以得用减法,比如找到画中符合斜率为-1和1的Y线或者B线将其还原成X。
不管输入的矩阵是正方形还是长方形,都从第一行第一列的格子开始向右遍历所有格子,假设当前格子是array[i][j],分三种情况讨论:
1、当前格子是黄色‘Y’,说明这里必须有一次操作,操作数+1,那么右下找斜率为-1的直线,即一直找array[i+1][j+1]的格子(确保不出界):
①、如果array[i+1][j+1]的格子也是Y,那么将其标记为X还原;
②、如果是G,将其还原为G-Y即B。
然后继续往-1斜率找,直到遇到的格子既不是Y也不是G
2、类似Y的处理,当前格子是蓝色‘B’,说明这里必须有一次操作,操作数+1,那么左下找斜率为1的直线,即一直找array[i+1][j-1]的格子(确保不出界):
①、如果array[i+1][j-1]的格子也是B,那么将其标记为X还原;
②、如果是G,将其还原为G-B即Y。
然后继续往1斜率找,直到遇到的格子既不是B也不是G
3、当前格子是绿色‘G’,说明这里必须有两次操作,操作数+2,绿色得当黄色和蓝色两种同时处理,右下找斜率为-1的直线,左下找斜率为1的直线,然后处理同上述1和2。
代码
#include <iostream>
using namespace std;
int main(void) {
// 记录结果:操作数(绘制路径数)
int ans = 0;
// n*m,n行m列
int n, m;
cin >> n >> m;
char array[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> array[i][j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (array[i][j] == 'Y') {
// 路径加一
ans++;
int a = i + 1;
int b = j + 1;
while (a < n && b < m) {
// 只要不出界,就往右下遍历
if (array[a][b] == 'Y') {
array[a][b] = 'X';
a++;
b++;
} else if (array[a][b] == 'G') {
array[a][b] = 'B';
a++;
b++;
} else {
// 只有遇到Y和G才继续遍历,遇到X和B都停
break;
}
}
}
if (array[i][j] == 'B') {
// 蓝色处理类似黄色
ans++;
int a = i + 1;
int b = j - 1;
while (a < n && b >= 0) {
// 只要不出界,就往左下遍历
if (array[a][b] == 'B') {
array[a][b] = 'X';
a++;
b--;
} else if (array[a][b] == 'G') {
array[a][b] = 'Y';
a++;
b--;
} else {
break;
}
}
}
if (array[i][j] == 'G') {
// 绿色当黄色+蓝色处理
ans += 2;
int a = i + 1;
int b = j + 1;
while (a < n && b < m) {
if (array[a][b] == 'Y') {
array[a][b] = 'X';
a++;
b++;
} else if (array[a][b] == 'G') {
array[a][b] = 'B';
a++;
b++;
} else {
break;
}
}
int c = i + 1;
int d = j - 1;
while (c < n && d >= 0) {
if (array[c][d] == 'B') {
array[c][d] = 'X';
c++;
d--;
} else if (array[c][d] == 'G') {
array[c][d] = 'Y';
c++;
d--;
} else {
break;
}
}
}
}
}
cout << ans << endl;
}
代码通过样例100%
第二题——贪吃的小Q
题意
小Q有M块巧克力,要在N天内吃完。小Q决定每天吃的巧克力数量不少于前一天吃的一半,又不想N天内的任何一天没有巧克力吃,请问小Q第一天最多能吃多少块巧克力。
输入描述:
两个正整数,表示天数N(N<=50000)和巧克力数量M(N<=M<=100000)。输出描述:
一个正整数,表示小Q第一天最多能吃多少块巧克力
输入
3 7
输出
4
思路
根据题意,最理想的情况当然是,最后一天只吃M/(2^N-1)块,倒数第二天吃最后一天的两倍,倒数第三天吃倒数第二天的两倍,这样第一天肯定能吃最多,问题是,巧克力足够还好说,如果不足够是没法这样吃的。
由于巧克力可能数量不够的限制,我们不要从最后一天为1倒回去,而是从第一天往最后一天推,第二天永远是前一天的一半(如果前一天是奇数,则为一半+1,相当于四舍五入,比如前一天吃3,第二天得吃2,不能吃1),然后再把所有天数吃的巧克力总数加起来与M比较,如果比M小,说明符合题意。
优化算法速率:
①、由于是第一天要吃最多,所以第一天往最后一天推的时候,第一天应该从大往小遍历(这样能提高找到答案的效率,因为从第一天只吃一块开始判断是没意义的)。
②、而且从大往小的这个“大”也未必要从M开始,比如输入样例4 13,如果从第一天吃13块开始判断,都是没意义的。这里考虑到最最理想情况也就是每天都是2的幂次数。比如四天分别吃8M/15、4M/15、2M/15、M/15这样,你会发现这种情况第一天也就比其他天加起来多了M/(2^N-1)块而已,
所以我们从M/2+M/(2^N-1)开始遍历。
代码
#include<iostream>
#include<math.h>
using namespace std;
int main(void) {
int n, m;
cin >> n >> m;
int ans = m;
// 计算多出来的块数
int ex = m/(pow(2,n)-1);
// 若n为1,直接返回m
if (n > 1) {
for (int i = m/2+ex; i >= 1; i--) {
// i为第一天吃的块数
int temp = i;
// he保存各天数吃的巧克力总数
int he = i;
for (int j = 1; j < n; j++) {
if ((temp&1) == 0) {
temp = temp / 2;
} else {
temp = temp / 2 + 1;
}
he += temp;
}
if (he <= m) {
// 如果加起来的总数小于m,说明找到答案,停止循环,节省时间
// 保存第一天吃的块数
ans = i;
break;
}
}
}
cout << ans << endl;
}
代码通过样例未知,>=80%
第三题——纸牌游戏
题意
牛和羊玩纸牌游戏,有n张牌,每一张牌写着数字a[i]。牛先抽牌,然后是羊,轮流抽,每次一张。因为得分等于抽到的纸牌数字总和,然后牛和羊都采取最优策略,游戏结束后牛得分减去羊的得分等于多少。
输入描述:
第一行为一个正整数n(1<=n<=10^5),表示纸牌的数量。
第二行为n个正整数a[i](1<=a[i]<=10^9),表示每张纸牌上的数字。
输出描述:
一个整数,表示游戏结束后牛得分减去羊的得分等于多少。
输入
3
2 7 4
输出
5
思路
最优策略,所以每人每次都要挑数组中最大的那个数加到自己的分数中去,维护两个变量作为牛和羊的分数。
然后对整个数组排序(我这里是降序快排),牛先抽,所以他的分数是,快排后的数组的所有奇数位上的数之和,羊的分数则是所有偶数位上的数之和。
代码
#include<iostream>
using namespace std;
void quicksort(int a[], int left, int right) {
int i, j, temp;
if (left > right) {
return;
}
temp = a[left];
i = left;
j = right;
while(i != j) {
while(a[j]<=temp && i<j)
j--;
while(a[i]>=temp && i<j)
i++;
if (i < j) {
int num = a[i];
a[i] = a[j];
a[j] = num;
}
}
a[left] = a[i];
a[i] = temp;
quicksort(a, left, i-1);
quicksort(a, i+1, right);
}
int main(void) {
int n;
scanf("%d", &n);
int a[n];
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
quicksort(a, 0, n-1);
int niu = 0, yang = 0;
for (int i = 0; i <= n-1; i++) {
if ((i&1) == 0) {
// 如果为偶数位
niu += a[i];
} else {
yang += a[i];
}
}
printf("%d\n", niu-yang);
return 0;
}
代码通过样例100%
总结
难度其实不难,但是对我来说,在那种考试状态要冷静下来想出最优解法还是挺难的。
再接再厉!