这场题意和做法应该都比较明确,比较偏考验代码功底,算法的细节其实不多,所以讲解比较少,基本在代码中,代码中有比较详细的注释。如果还不能明白,可以私信问我或者回复。
Problem A:
给7张麻将牌(长度为2的字符串),问除花牌部分能不能组成"七星不靠"(这个可以自行百度)。
做法:应该挺显然的,处理下万饼条分别属于147,258,369的哪一类,再检查下类间是否互斥即可,具体看代码吧。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int T; scanf("%d", &T); while(T--) {
vector<int> v[3];//存放万饼条的数字
//输入
for(int i = 0; i < 7; i++) {
char p[5]; scanf("%s", p);
if(p[1] == 'T')v[0].push_back(p[0] - '0');
if(p[1] == 'B')v[1].push_back(p[0] - '0');
if(p[1] == 'W')v[2].push_back(p[0] - '0');
}
int fl = 1;
//特判缺门的情况
for(int i = 0; i < 3; i++)if(v[i].empty()) {
puts("NO");
fl = 0;
break;
}
if(!fl)continue;
//排序
for(int i = 0; i < 3; i++)sort(v[i].begin(), v[i].end());
//判断万饼条内部是否满足隔3或者隔6的条件
for(int i = 0; i < 3; i++)for(int j = 1; i < v[i].size(); j++) {
if(v[i][j] - v[i][j - 1] == 3 || v[i][j] - v[i][j - 1] == 6);
else fl = 0;
}
//判断万饼条间元素对3取模是否互相独立
int cc[3] = {0};
for(int i = 0; i < 3; i++)cc[v[i][0] % 3]++;
for(int i = 0; i < 3; i++)if(!cc[i])fl = 0;
puts(fl ? "YES" : "NO");
}
return 0;
} Problem B:
给个n*n矩阵,每次消除一行一列,要求消除的行列和尽可能大,如果有多个方案可选,选择行号尽量小的,同行就选列号尽量小的。
消除后矩阵会变为(n-1)*(n-1)矩阵,继续消除操作。
问n次操作选择的行列方案分别是多少?
n范围500。
做法:预处理每行每列的和,每次消除枚举行列,计算方案的和,选出最佳的。
消除后将矩阵暴力的合并起来,并更新行列和的数组。
总体复杂度控制在立方级别就可以。
#include<bits/stdc++.h>
using namespace std;
const int N = 505;
int ph[N], pl[N]; //ph表示每列的和,pl表示每行的和
int a[N][N]; //存放矩阵元素
int main() {
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++){
//输入矩阵元素
scanf("%d", &a[i][j]);
//顺带处理每行每列的和
pl[i] += a[i][j], ph[j] += a[i][j];
}
for(int _ = 0; _ < n; _++) {
//计算每个坐标丢十字斩的的得分,求出题目要求的坐标
int x = 1, y = 1;
for(int i = 1; i <= n - _; i++)for(int j = 1; j <= n - _; j++) {
int pre = pl[x] + ph[y] - a[x][y];
int now = pl[i] + ph[j] - a[i][j];
if(now > pre)x = i, y = j;
}
printf("%d %d", x, y);
//处理当前每行每列的和
for(int i = 1; i <= n - _; i++)ph[i] -= a[x][i];
for(int i = 1; i <= n - _; i++)pl[i] -= a[i][y];
//将矩阵移位,处理的两个数组也移位
for(int i = 1; i < n - _; i++)for(int j = 1; j < y; j++)a[i][j] = a[i + 1][j];
for(int i = 1; i < x; i++)for(int j = y; j < n - _; j++)a[i][j] = a[i][j + 1];
for(int i = x; i < n - _; i++)for(int j = y; j < n - _; j++)a[i][j] = a[i + 1][j + 1];
for(int i = x; i < n - _; i++)pl[i] = pl[i + 1];
for(int i = y; i < n - _; i++)ph[i] = ph[i + 1];
}
} Problem C:
有若干个任务,每个任务可能有子任务,给出每个任务的开始与结束时间,保证某个任务的子任务一定在那个任务期间完成,且每个任务期间不会有其他非子任务的任务插入。
问哪个任务的纯用时(任务时间减去所有子任务时间)最大。
做法:可以看出任务在这样的限制下,可以用一个栈来处理,并计算总时长,那么只需要再处理所有任务的子任务时长,就可以得出答案了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int T; scanf("%d", &T);
while(T--) {
map<int, int> M; //存放子任务的时长和
stack<pair<int, int> > S; //存放当前父任务序列
int n; scanf("%d", &n);
int id = -1, maxtime = 0;
for(int i = 0; i < n; i++) {
int t, e, s; scanf("%d%d%d", &t, &e, &s);
if(s == 0) S.push(make_pair(t, e));//如果任务是开始,丢入栈中
else {
//任务结束,栈顶必定是这个任务的开始状态
//计算任务时长
int nowtime = t - S.top().first;
//判断当前任务的自身用时是否大于当前最大值
if(nowtime - M[e] > maxtime || nowtime - M[e] == maxtime && id > e)maxtime = nowtime - M[e], id = e;
S.pop();
//更新父任务的子任务时长和
if(!S.empty())M[S.top().second] += nowtime;
}
}
//输出答案
printf("%d", id);
}
return 0;
} Problem D:
给定一个地图,规模最大50*50,有10个以内的宝箱,在地图0-9的位置上,主人公位置在*号处。
主人公每一步的行动逻辑如下:
找出离他曼哈顿距离最近的宝箱。
枚举方向,优先级上下左右,如果枚举到的方向可以让离那个宝箱的最短路缩短,那么就走那个方向,其他方向不再枚举。
如果这样的行动逻辑可以拿走所有的宝箱,输出步长,否则输出-1。
做法:直接看代码吧,注释里写的很详细了。
-1的情况有两种,一种是死循环了,这个可以通过加tag判断。
一种是宝箱不可达,这个在处理最短路的过程中是可以发现的。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
const int pos[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1}; //预处理上下左右
char Map[N][N]; //存放地图
int Box[N][2]; //存放宝箱坐标
int dis[N][N][N]; //存放每个宝箱到地图其他点的最短路
int pick[N]; //标记宝箱是否已经被拿过
int fl[N][N]; //标记地图中某个点是否被走过
int sx, sy; //人物当前的坐标
void init() {
memset(Map, 0, sizeof Map);
memset(pick, 0, sizeof pick);
memset(fl, 0, sizeof fl);
}
int main() {
int T; scanf("%d", &T); while(T--) {
//初始化全局数组
init();
//读入地图
int n, m; scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)scanf("%s", Map[i] + 1);
//搜索地图,查看宝箱坐标和起始点坐标
int cnt = 0;
for(int i = 1; i <= n; i++)for(int j = 1; i <= m; i++) {
if(Map[i][j] == '*')sx = i, sy = j, Map[i][j] = '.';
else if(isdigit(Map[i][j]))Box[Map[i][j] - '0'][0] = i, Box[Map[i][j] - '0'][1] = j, cnt++, Map[i][j] = '.';
}
//bfs处理出每个宝箱的点到地图中所有格子的最短路
for(int i = 0; i < cnt; i++) {
memset(dis[i], -1, sizeof dis[i]);
int x = Box[i][0], y = Box[i][1];
dis[i][x][y] = 0;
queue<pair<int, int> > Q; Q.push(make_pair(x, y));
while(!Q.empty()) {
int nowx = Q.front().first, nowy = Q.front().second;
for(int p = 0; p < 4; p++) {
int nextx = nowx + pos[p][0];
int nexty = nowy + pos[p][1];
if(Map[nextx][nexty] == '.' && dis[i][nextx][nexty] == -1) {
dis[i][nextx][nexty] = dis[i][nowx][nowy] + 1;
Q.push(make_pair(nextx, nexty));
}
}
Q.pop();
}
}
int ccnt = 0, len = 0; //分别表示当前拿走的宝箱数和步长
//开始行动
while(ccnt < cnt) {
int x = -1, mindis = 1e9;
//计算目标宝箱
for(int i = 0; i < cnt; i++) {
if(pick[i])continue;
int mdis = abs(Box[i][0] - sx) + abs(Box[i][1] - sy);
if(mdis < mindis)x = i, mindis = mdis;
}
//判断目标宝箱是否可达,或者当前这个格子曾经走过
if(dis[x][sx][sy] == -1 || fl[sx][sy]) {
puts("-1");
break;
}
fl[sx][sy] = 1;//标记当前格子已被走过
//枚举上下左右方向,找出下一步坐标
for(int p = 0; p < 4; p++) {
int nx = sx + pos[p][0];
int ny = sy + pos[p][1];
if(Map[nx][ny] != '.')continue;
if(dis[x][nx][ny] < dis[x][sx][sy]) {
len++, sx = nx, sy = ny;
break;
}
}
//判断是否已经到达宝箱
if(sx == Box[x][0] && sy == Box[x][1]) {
ccnt++, pick[x] = 1;
//将标记清空
memset(fl, 0, sizeof fl);
}
}
//输出答案
if(ccnt == cnt)printf("%d\n", len);
}
return 0;
}

全部评论
(3) 回帖