文章目录
C 硬币游戏
题目描述
Alice和Bob想到了一种硬币游戏。在他们面前有一排硬币,每个硬币有正面和反面两种状态,规定正面为0反面为1。现在Alice和Bob想通过轮流操作使所有的硬币都处于同一种状态。一个人在操作结束后,如果已经处于这种局面,那么他就赢得游戏。每次操作可以选取连续不超过k个硬币,选取的硬币必须同为正面或者同为反面,比如“111”, “00”,”1”,但是不能不选硬币。选完之后会将硬币反转并放回原来的位置。两个人都会采取最优策略,并且Alice选择先手。如果Alice获胜,请输出“Alice“,Bob获胜则输出”Bob“,如果谁都无法获胜,请输出 ” 😦 “ 。
输入输出
第一行输入两个整数n, k,分别表示硬币个数和每次最多拿取的硬币个数。
接下来输入一行字符串,只包含‘0’,‘1’两种字符,字符串大小为n。
输出"Alice"or"Bob"。
数据范围
1≤n≤1000000
0<k≤n
输入样例
4 2
1100
输出样例
Alice
思路
这道题是一个博弈游戏,题目大致意思就是给一个字符串,然后两人轮流操作,每次操作可以选择一个连续且相同的字符串,字符串长度不能超过k,然后将此段字符串翻转。如果谁操作后字符串全为0或1时,那么他将获胜。
这个博弈有三种结果:1、Alice获胜;2、Bob获胜;3、平局。
先来判断Alice获胜的情况,Alice是先手,当他首先执行一次操作后,如果他没有获胜的话,那么Bob只需在重复一次Alice的操作,那么Alice将无法获胜。这时我们就得出了Alice的获胜情况,即:如果Alice能一次操作直接获胜,那么他必胜,否则将不会获胜。
再来判断Bob获胜的情况,Bob是后手,当Alice第一次操作后没有获胜,到Bob开始。此时Bob就会遇到和Alice同样的处境,即:如果当前不能直接获胜,那么将无法获胜。因为第三步Alice重复Bob的操作,那么Bob将无法获胜。
那么最后平局的情况就显而易见了,当前两步无法确定出胜负的话,那么必定是平局。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
// #include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
typedef pair<char, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 100010;
int x0, x1, y0, y1;
bool flag;
int main(void){
IOS
int n, k; cin >> n >> k;
string s; cin >> s;
if(s[0] == '0') {
flag = false; x0 ++ ;
} else {
flag = true; x1 ++ ;
}
for(int i = 1; i < s.size(); i ++ ) { //计算连续相同子字符串的长度及个数
if(s[i] == '0' && !flag) {
x0 ++ ;
if(x0 > k) {
x0 -= k; y0 ++ ;
}
} else if(s[i] == '0' && flag) {
y1 ++ ;
x1 = 0;
x0 ++ ;
flag = false;
} else if(s[i] == '1' && flag) {
x1 ++ ;
if(x1 > k) { //若长度大于k,它就相当于2个及以上的字符串,不能当作一个,因为无法一次翻转
x1 -= k; y1 ++ ;
}
} else if(s[i] == '1' && !flag) {
flag = true;
y0 ++ ;
x0 = 0;
x1 ++ ;
}
}
if(x0) y0 ++ ;
if(x1) y1 ++ ;
if(y0 == 0 || y1 == 0) {
if(n > k) cout << "Bob" << endl;
else cout << "Alice" << endl;
} else if(y0 == 1 || y1 == 1) {
cout << "Alice" << endl;
} else if(s == "0101" || s == "1010" || s == "1100" || s == "0110" || s == "0011" || s == "1001") {
cout << "Bob" << endl;
} else {
cout << ":(" << endl;
}
return 0;
}
E 动物朋友
题目描述
已知有n个动物朋友排成一排,每个动物朋友都有一个正整数的快乐值,涛涛每次会和连续的动物朋友玩,并且获得这些动物朋友快乐值的和的快乐,而涛涛是个完美主义者,他觉得快乐值刚好是m时候才是快乐的,现在请问有多少种选择方式,使得所选的连续的动物朋友的快乐值刚好为m。
输入输出
第一行输入n和m。
第二行输入n个正整数,第i个代表第i个动物朋友的快乐值。
输出一个整数,表示可能存在的选法数量,如果没有,就输出0;
数据范围
1<=n<=1e6
1<=m<=1e6
1<=ai<=1e5
输入样例
11 45
1 4 1 9 19 8 10 8 1 2 3
输出样例
1
思路
这道题是求区间和为m的个数。输出个数,没有则输出0。
因此我们可以使用一个队列储存区间和。首先遍历数组,当队列的区间和小于m时,将当前遍历到的元素添加到队列里,然后将队列里的和更新,然后判断和是否为m,是则ans++;若和大于m,则依次删除队头元素,直至和小于等于m。最后输出ans即可。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
typedef pair<char, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 1e6 + 10;
int a[N], b[N], ans;
queue<int> q;
int main(void){
IOS
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
}
int sum = 0;
for(int i = 1; i <= n + 1; i ++ ) {
while(q.size() && sum > m) {
sum -= a[q.front()];
q.pop();
}
if(sum == m) ans ++;
q.push(i);
sum += a[i];
}
cout << ans << endl;
return 0;
}
F 松鼠排序
题目描述
松鼠宝宝有一排n个大小不一的坚果,松鼠宝宝想把坚果从小到大排序,每次他会选择两个坚果a和b每次花费1点力气把这两个坚果交换,爱动脑筋的松鼠宝宝想知道他排完这n个坚果一共需要花费的最少力气是多少?
输入输出
第一行一个整数n代表坚果数
接下来一行n个整数代表每个坚果的大小(每个坚果大小都不一样,即大小为1-n的一个排列)
一行输出代表松鼠宝宝花费的最小力气。
数据范围
1<=n<=1e5
1<=x<=n
输入样例
3
3 2 1
输出样例
1
思路
这道题就是将一组乱序的数字升序排列,但是每次只能选中两个数字并交换位置,求最少交换次数。
这道题想要求最少交换次数,那么我们就要知道按照什么规则进行排序才能使交换次数最少。
容易想到每次交换与升序排列后位置不相同的数字,若该位置升序排列后为x,则应将该位置的数字与x替换,这样能使得交换次数最少。
实现过程中,每次将当前位置错误的数字与正确数字相替换,同时将被替换的数字下标改为新位置的下标。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
typedef pair<int, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 1e5 + 10;
int ans, b[N];
pir a[N];
int main(void){
IOS
int n; cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i].first;
a[i].second = i;
b[i] = a[i].first;
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i ++ ) {
if(b[i] != a[i].first) {
ans ++;
b[a[i].second] = b[i];
a[b[i]].second = a[i].second;
b[i] = a[i].first;
}
}
cout << ans << endl;
return 0;
}
G Reverse
题目描述
给定一个长度为n的01串,你需要选择一段任意长度(可以为0)的区间对其翻转,翻转后,求最长的一段连续的全是1的区间的长度。
输入输出
链接:https://ac.nowcoder.com/acm/contest/61132/G
来源:牛客网
输入共2行。
第一行一个整数n
第二行一个长度为n的01序列。
输出一个整数,表示最长的长度。
数据范围
1 <= n <= 1e6
输入样例
10
0111001011
输出样例
5
样例说明
翻转区间[5,10],翻转为0111110100。
思路
字符串中任意两个不相邻子序列可以通过上述的翻转方式相连。
然后本题所求就变成:求字符串中前两长的子1串长度相加。
因此只需求出所有子1串的长度,然后排序,将最大的两个相加即可。
但是字符串中可能只有0,这需要特判一下。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
typedef pair<char, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 1e6 + 10;
int a[N];
int main(void){
IOS
int n; cin >> n;
string s; cin >> s;
int tot = 0;
for(int i = 0; i < n; i ++ ) {
int sum = 0;
while(s[i] == '1') {
sum ++;
i ++;
}
a[++tot] = sum;
}
sort(a + 1, a + tot + 1);
// for(int i = 1; i <= tot; i ++ ) cout << a[i] << " ";
int ans = 0;
if(tot == 0) ans = 0;
else ans = a[tot] + a[tot - 1];
cout << ans << endl;
return 0;
}
H 迷宫探险
题目描述
在与boss的最终决战之后,小蓝来到了冒险的最后一关,在他面前有一个nm的迷宫,迷宫中道路用’.’表示,墙壁则由‘#’表示。小蓝初始在[1,1]的位置,他只有到达[n,m]才能开启最终的宝藏。小蓝现在迫不及待的想要开启宝藏,所以他想最短的时间内走出迷宫。现在迷宫内有一种特殊的装置 –“弹射器”。弹射器的格子用’’表示。当走到有弹射器的一格时,小蓝必须选择一个方向,弹射器会让他沿着这个方向弹射 x个距离,不同弹射器的弹射距离可以不同。弹射后的格子如果超过迷宫边界或者是墙壁则不能选择这个方向。小蓝现在可以向上下左右四个方向走,每走一个格子需要消耗一个单位时间,弹射则不消耗时间。求最短需要多少时间小蓝才能走出迷宫。如果无法到达终点,输出-1。
弹射器的数量,位置和弹射距离将在输入中给出。起点和终点一定不是弹射器。
输入输出
第一行两个整数 n, m,接下来n行,每行m个只包含 ’ . ’ , ’ * ’ , ’ # ’ 的字符描绘迷宫。
接下来一行一个整数k,下面的k行每行三个整数x, y, w表示在[x,y]格子的弹射器能弹射的距离。
输出一行一个整数。
数据范围
(2≤n≤3000,2≤m≤3000, n*m≤500000, 0≤k, w在int范围内)
输入样例
3 2
.*
#.
..
1
1 2 2
输出样例
1
思路
这道题可以用bfs来写,但是搜索过程不好处理。然后通过题目描述可以发现这道题是求最短路径,所以我们可以使用求最短路算法dijkstra来写。
使用dijkstra来写的好处是可以保持局部最优解,即保持我们储存的从一个点到另一个点的距离始终最短,然后通过局部最优达到全局最优即最短路径。
要达到保存两点间的最短路径,同时去除两点间的重复路径,容易想到使用STL里面具有去重排序的set集合容器,将点的坐标(x, y) 和距离起点的距离放入集合内部。
在代码具体实现过程中,按照dijkstra的思想,先判断当前位置是否为弹射器,然后按照规则判断四个方向,并更新新的坐标与起点的距离。要注意当通过弹射器移动时不加步数。然后按照正常移动判断前后左右,并更新新的点的位置。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
#define int long long
typedef pair<int, int> pir;
const int mod = 0x7f7f7f7f;
const int inf = 1e18;
const int N = 3005;
char s[N][N];
int b1[N][N], b2[N][N];
int nex[4] = {1, -1, 0, 0};
int ney[4] = {0, 0, 1, -1};
signed main(void){
IOS
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i ++ ) { //输入并初始化b1[]
for(int j = 1; j <= m; j ++ ) {
cin >> s[i][j];
b1[i][j] = inf;
}
}
int q1; cin >> q1;
while(q1 -- ) {
int x, y, z; cin >> x >> y >> z;
b2[x][y] = z;
}
set<pair<int, pir>> q;
q.insert({0, {1, 1}});
b1[1][1] = 0;
while(!q.empty()) {
pair<int, pir> d = *q.begin();
q.erase(q.begin());
if(s[d.second.first][d.second.second] == '*') { //判断一下当前位置为弹射器的情况
for(int i = 0; i < 4; i ++ ) { //枚举四个方向
int x1 = d.second.first + nex[i] * b2[d.second.first][d.second.second];
int y1 = d.second.second + ney[i] * b2[d.second.first][d.second.second];
if(x1 >= 1 && x1 <= n && y1 >= 1 && y1 <= m && s[x1][y1] != '#') {
if(b1[x1][y1] > d.first) {
q.erase({b1[x1][y1], {x1, y1}});
b1[x1][y1] = d.first;
q.insert({b1[x1][y1], {x1, y1}});
}
}
}
continue;
}
for(int i = 0; i < 4; i ++ ) { //枚举在当前位置往下一步走的情况
int x1 = d.second.first + nex[i];
int y1 = d.second.second + ney[i];
if(x1 >= 1 && x1 <= n && y1 >= 1 && y1 <= m && s[x1][y1] != '#') {
if(b1[x1][y1] > d.first + 1) { //更新最短路径
q.erase({b1[x1][y1], {x1, y1}});
b1[x1][y1] = d.first + 1;
q.insert({b1[x1][y1], {x1, y1}});
}
}
}
}
if(b1[n][m] == inf) cout << "-1" << endl; //b1[n][m] = inf,说明从起点出发无法到达终点
else cout << b1[n][m] << endl;
return 0;
}
J 合唱比赛
题目描述
河南农业大学信管学院举办一年一度的合唱比赛,目前你是评委之一,剩下还有其他的n位评委,给定一个正整数n和n个正整数表示这n个评委给目前在表演的团队的分数,评分规则为在所有评委(包括你)的分数中去掉一个最高分和最低分,剩下的取平均值(总共n-1个值),现在你可以参与评分(1~100之间的整数),问最终结果会在什么区间内,用两个数表示这个区间,结果保留6位小数。
输入输出
第一行给定一个正整数n
接下来一行给定n个整数表示n个评委的分数
输出两个保留六位的小数l,r表示答案。
数据范围
2<=n<=1000
输入样例
4
3 5 9 13
3
80 90 100
输出样例
5.666667 9.000000
85.000000 95.000000
思路
求数组平均值,保留6位小数。
当求最大平均值时,需要自己大的分要比当前最高分大;求最小平均值时,同理,需要自己的分要比最小数下。
题目要求去除最大最小值,则求最大平均值时只去除最小值,求最小平均值时之去除最小值。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
// #define endl '\n'
typedef pair<char, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 100010;
int a[N];
int main(void){
// IOS
int n; cin >> n;
int sum = 0;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
sum += a[i];
}
sort(a + 1, a + n + 1);
sum = sum - a[1] - a[n];
int minn = sum + a[1];
int maxx = sum + a[n];
n--;
printf("%.6lf %.6lf\n", (double)minn / n, (double)maxx / n);
return 0;
}
K 以撒和隐藏房间
题目描述
以撒又一次的逃进了地下室
地下室可以看作一个n*m的矩阵的迷宫,其中有些格子是有门相连房间,有些则是无法通过的墙壁。以撒发现其中一些墙壁似乎是空心的,可以通过爆炸打开隐藏的房间,而隐藏房的生成有一定的规律,以撒认为一个墙壁格子在满足以下所有情况时可能会是隐藏房间:
1, 该墙壁格子和三个普通房间相邻
2, 在满足1条件的情况下,不能和boss房间相邻
但是以撒正在和萌死戳交战,
现在你需要编写程序告诉他是否存在可能是隐藏房间的格子。
如果存在,输出两行,第一行是一个YES,第二行输出可能为隐藏房间的格子的数量
如果不存在,输出NO
输入输出
第一行两个整数n,m
然后是一个n*m矩阵,表示地图状态,0表示墙壁,1表示房间,2表示boss房间
如果存在,输出两行,第一行是一个YES,第二行输出可能为隐藏房间的格子的数量
如果不存在,输出NO
数据范围
3< m , n <= 1000
输入样例
3 3
001
110
211
输出样例
YES
1
思路
这道题直接爆搜就好了,n、m的值小于1000,然后每个点有四种移动方式,所以这道题的时间复杂度只有O(4nm),即1000 10004=4000000,1s时间很充足。
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define ull unsigned long long
#define endl '\n'
typedef pair<char, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 1005;
bool flag = false;
string s[N];
int ne[2][4] = {{0, 0, 1, -1}, {1, -1, 0, 0}};
int main(void){
IOS
int n, m; cin >> n >> m;
for(int i = 0; i < n; i ++ ) {
cin >> s[i];
}
int res = 0;
for(int i = 0; i < n; i ++ ) {
for(int j = 0; j < m; j ++ ) {
if(s[i][j] == '1' || s[i][j] == '2') continue;
int sum = 0;
bool ff = true;
for(int k = 0; k < 4; k ++ ) {
int x = i + ne[0][k];
int y = j + ne[1][k];
if(x < 0 || x >= n || y < 0 || y >= m) continue;
if(s[x][y] == '1') sum ++;
if(s[x][y] == '2') {
ff = false; break;
}
}
if(ff && sum == 3) {
res ++;
// cout << i << j << endl;
}
}
}
if(res) {
cout << "YES" << endl << res << endl;
} else {
cout << "NO" << endl;
}
return 0;
}