第十一章 模拟
AcWing 1480. 电梯
问题描述
-
问题链接:AcWing 1480. 电梯、原题链接
分析
-
记录上一次电梯所在的楼层
last
,然后根据当前所在的楼层cur
更新用时。 -
注意:每个停止的楼层都要消耗
5s
。
代码
- C++
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int res = 0;
int last = 0; // 上次停在的楼层
for (int i = 0; i < n; i++) {
int cur;
cin >> cur;
if (last < cur) res += (cur - last) * 6;
else res += (last - cur) * 4;
res += 5;
last = cur;
}
cout << res << endl;
return 0;
}
AcWing 1483. 世界杯投注
问题描述
-
问题链接:AcWing 1483. 世界杯投注、原题链接
分析
- 本题相当于给一个
3x3
的矩阵,我们需要从每一行中选取一个最大的数,输出所在的列,最后将选出的三个数按照题目给出的公式计算利润。
代码
- C++
#include <iostream>
using namespace std;
int main() {
double res = 1;
for (int i = 0; i < 3; i++) {
double w, t, l;
cin >> w >> t >> l;
double x = max(w, max(t, l));
if (x == w) printf("W ");
else if (x == t) printf("T ");
else printf("L ");
res *= x;
}
printf("%.2lf\n", (res * 0.65 - 1) * 2);
return 0;
}
AcWing 1486. 排队等候
问题描述
-
问题链接:AcWing 1486. 排队等候、原题链接
分析
-
银行有
n
个窗口,我们可以开n
个队列模拟这n
个窗口,队列中存储每个人的完成时间(相对于0
,最后还要加上8
)。 -
每次新来一个人,我们如何将其插入队列中呢?可以另外使用一个数组
sum
,sum[i]
中记录了当前第i
个队列所有人被服务结束的时间。 -
首先我们找到新来的人需要插到的队列
t
,如果有未满的队列,找到第一个未满的队列,将新来的人插入,如果队列都是满的话,找到最先有一个人完成的队列,将新来的人插入队尾即可。 -
对于在
8:00~16:59
之间来的人服务结束时间存入哈希表中,方便之后查询。
代码
- C++
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 20;
int n, m, k, Q;
queue<int> q[N];
int sum[N];
int main() {
cin >> n >> m >> k >> Q;
unordered_map<int, int> hash;
for (int i = 1; i <= k; i++) {
int s;
cin >> s; // 第i个人服务用时
int t = 0; // 找到第i个人应该插入到的队列t
for (int j = 0; j < n; j++) {
if (i <= n * m) {
if (q[j].size() < q[t].size())
t = j;
} else {
if (q[j].front() < q[t].front())
t = j;
}
}
sum[t] += s;
if (i > n * m) q[t].pop();
q[t].push(sum[t]);
if (sum[t] - s < 540) hash[i] = sum[t];
}
while (Q--) {
int id;
cin >> id;
if (hash.count(id)) printf("%02d:%02d\n", hash[id] / 60 + 8, hash[id] % 60);
else puts("Sorry");
}
return 0;
}
AcWing 1515. U 形 Hello World
问题描述
分析
- 根据题意可知:
{ n = n 1 + n 2 + n 3 − 2 n 1 = n 3 n 1 ≤ n 2 \begin{cases} n = n_1 + n_2 + n_3 - 2 \\ n_1 = n_3 \\ n_1 \le n_2 \end{cases} ⎩⎪⎨⎪⎧n=n1+n2+n3−2n1=n3n1≤n2
- 根据 n 1 = n 3 n_1=n_3 n1=n3,可知 n = 2 n 1 + n 2 − 2 n=2n_1+n_2-2 n=2n1+n2−2,即 n 2 = n + 2 − 2 n 1 n_2 = n+2-2n_1 n2=n+2−2n1,带入最后一个不等式中,可以得到:
n 1 ≤ n + 2 3 n_1 \le \frac{n+2}{3} n1≤3n+2
-
因为要求 n 1 n_1 n1 最大,上式向下取整就是 n 1 n_1 n1的取值,然后就可以得到另外两个数的值。
-
之后开一个二维数组,将对应的字母放置到对应的位置输出即可。
代码
- C++
#include <iostream>
using namespace std;
const int N = 100;
char g[N][N];
int main() {
string s;
cin >> s;
int n = s.size();
int n1 = (n + 2) / 3;
int n2 = n - 2 * n1 + 2;
int k = 0;
for (int i = 0; i < n1; i++) g[i][0] = s[k++];
for (int i = 1; i < n2; i++) g[n1 - 1][i] = s[k++];
for (int i = n1 - 2; i >= 0; i--) g[i][n2 - 1] = s[k++];
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++) {
if (g[i][j]) cout << g[i][j];
else cout << ' ';
}
cout << endl;
}
return 0;
}
AcWing 1525. 独一无二
问题描述
-
问题链接:AcWing 1525. 独一无二、原题链接
分析
- 两次遍历序列即可,第一次遍历统计出每个数字出现的次数,第二次遍历查看第一次出现一次的数字,如果不存在输出
None
即可。
代码
- C++
#include <iostream>
using namespace std;
const int N = 100010;
int n;
int a[N], c[N];
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
c[a[i]]++;
}
for (int i = 0; i < n; i++)
if (c[a[i]] == 1) {
printf("%d\n", a[i]);
return 0;
}
puts("None");
return 0;
}
AcWing 1526. 洗牌机
问题描述
-
问题链接:AcWing 1526. 洗牌机、原题链接
分析
- 初始序列为
1~54
,每次根据打乱数组q
进行洗牌,最终输出洗牌后的序列即可。
代码
- C++
#include <iostream>
#include <cstring>
using namespace std;
const int N = 60;
int k;
// a: 初始序列; q: 洗牌序列; w: 缓存序列
int a[N], q[N], w[N];
void print(int x) {
if (x <= 13) cout << 'S' << x;
else if (x <= 26) cout << 'H' << x - 13;
else if (x <= 39) cout << 'C' << x - 26;
else if (x <= 52) cout << 'D' << x - 39;
else cout << 'J' << x - 52;
}
int main() {
cin >> k;
for (int i = 1; i <= 54; i++) {
scanf("%d", &q[i]);
a[i] = i;
}
while (k--) {
memcpy(w, a, sizeof a);
for (int i = 1; i <= 54; i++) a[q[i]] = w[i];
}
for (int i = 1; i <= 54; i++) {
print(a[i]);
if (i != 54) cout << ' ';
}
return 0;
}
AcWing 1531. 课程学生列表
问题描述
-
问题链接:AcWing 1531. 课程学生列表、原题链接
分析
- 使用数组记录每个课程对应的学生即可。
代码
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 2510;
int n, k;
vector<string> lesson[N];
int main() {
scanf("%d%d", &n, &k);
char name[5];
for (int i = 0; i < n; i++) {
int cnt;
scanf("%s%d", name, &cnt);
while (cnt--) {
int id;
scanf("%d", &id);
lesson[id].push_back(name);
}
}
for (int i = 1; i <= k; i++) {
printf("%d %d\n", i, lesson[i].size());
sort(lesson[i].begin(), lesson[i].end());
for (auto id : lesson[i])
printf("%s\n", id.c_str());
}
return 0;
}
AcWing 1540. 主导颜色
问题描述
-
问题链接:AcWing 1540. 主导颜色、原题链接
分析
- 使用哈希表统计出现的次数,然后判断是否超过半数即可。
代码
- C++
#include <iostream>
#include <unordered_map>
using namespace std;
int n, m;
int main() {
scanf("%d%d", &n, &m);
unordered_map<int, int> hash; // (数据, 出现次数)
for (int i = 0; i < n * m; i++) {
int x;
scanf("%d", &x);
if (++hash[x] * 2 > n * m) {
printf("%d\n", x);
return 0;
}
}
return 0;
}
AcWing 1542. 老鼠和大米
问题描述
-
问题链接:AcWing 1542. 老鼠和大米、原题链接
分析
-
按照题目模拟一遍即可。
-
使用数组
cur
记录老鼠的顺序,cur[i] = j
表示第j
个老鼠排在位置i
上,则根据本轮顺序计算下一轮剩余的老鼠next
数组时,还需要存储每个老鼠的编号。
代码
- C++
#include <iostream>
#include <vector>
using namespace std;
const int N = 1010;
int n, m;
int w[N], ans[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> w[i];
vector<int> cur(n); // cur[i] = j表示第j个老鼠排在位置i上
for (int i = 0; i < n; i++) cin >> cur[i];
while (cur.size() > 1) {
vector<int> next;
int remain = (cur.size() + m - 1) / m; // 本轮过后剩余的老鼠数量
for (int i = 0; i < cur.size(); ) {
int j = min((int)cur.size(), i + m); // 考察区间[i, j)
int t = i;
for (int k = i; k < j; k++)
if (w[cur[k]] > w[cur[t]])
t = k;
next.push_back(cur[t]); // 最重的老鼠下次还要比较
for (int k = i; k < j; k++)
if (k != t)
ans[cur[k]] = remain + 1; // 不是本组中最重的老鼠的排名
i = j;
}
cur = next;
}
ans[cur[0]] = 1;
cout << ans[0];
for (int i = 1; i < n; i++) cout << ' ' << ans[i];
cout << endl;
return 0;
}
AcWing 1548. 才华与德行
问题描述
-
问题链接:AcWing 1548. 才华与德行、原题链接
分析
- 本题是一个多关键字排序,首先根据人的类型排序,每种类型内部根据得分降序排序,如果得分相同根据品德降序排序,如果还相同,则根据名称升序排列。
代码
- C++
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, L, H;
struct Person {
int id, moral, talent; // ID,德行,才华
int level; // 1~4分别代表:圣人、君子、愚人、小人
int total() const {
return moral + talent;
}
bool operator< (const Person &t) const {
if (level != t.level) return level < t.level;
if (total() != t.total()) return total() > t.total();
if (moral != t.moral) return moral > t.moral;
return id < t.id;
}
} p[N];
int main() {
scanf("%d%d%d", &n, &L, &H);
int cnt = 0; // 记录需要排序的人数
for (int i = 0; i < n; i++) {
int id, moral, talent;
scanf("%d%d%d", &id, &moral, &talent);
if (moral < L || talent < L) continue;
int level;
if (moral >= H && talent >= H) level = 1;
else if (moral >= H && talent < H) level = 2;
else if (moral < H && talent < H && moral >= talent) level = 3;
else level = 4;
p[cnt++] = {id, moral, talent, level};
}
sort(p, p + cnt);
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++)
printf("%d %d %d\n", p[i].id, p[i].moral, p[i].talent);
return 0;
}
AcWing 1551. A + B 和 C
问题描述
分析
-
本题考察数据在计算机中的表示。
-
两个正数相加的话如果溢出则会变为负数,此时
a+b
一定是大于c
的;两个负数相加的话如果溢出则会变为非负数(可能为0
,当两个数都是最小的负数时),此时a+b
一定是小于c
的。 -
最后如果不溢出的话,直接计算
a+b
和c
进行比较即可。
代码
- C++
#include <iostream>
using namespace std;
typedef long long LL;
bool check(LL a, LL b, LL c) {
LL t = a + b;
if (a > 0 && b > 0 && t < 0) return true;
if (a < 0 && b < 0 && t >= 0) return false;
return a + b > c;
}
int main() {
int T;
cin >> T;
for (int C = 1; C <= T; C++) {
LL a, b, c;
scanf("%lld%lld%lld", &a, &b, &c); // 不能用cin, PAT最后
printf("Case #%d: ", C);
if (check(a, b, c)) puts("true");
else puts("false");
}
return 0;
}
AcWing 1555. 数字黑洞
问题描述
-
问题链接:AcWing 1555. 数字黑洞、原题链接
分析
- 按照题目要求进行模拟即可。
代码
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> get(int n) {
int nums[4];
for (int i = 0; i < 4; i++) nums[i] = n % 10, n /= 10;
sort(nums, nums + 4);
int a = 0;
for (int i = 0; i < 4; i++) a = a * 10 + nums[i];
reverse(nums, nums + 4);
int b = 0;
for (int i = 0; i < 4; i++) b = b * 10 + nums[i];
return {b, a};
}
int main() {
int n;
cin >> n;
do {
auto t = get(n);
printf("%04d - %04d = %04d\n", t[0], t[1], t[0] - t[1]);
n = t[0] - t[1];
} while (n != 0 && n != 6174);
return 0;
}
AcWing 1566. 研究生入学
问题描述
-
问题链接:AcWing 1566. 研究生入学、原题链接
分析
-
本题只需要考虑每个人被录取到哪个大学即可,不需要考虑录取到哪个大学的哪个专业。
-
使用结构体存储每个同学,然后根据分数进行降序排序。然后依次考察每个分数段同学(要求这些同学的分数完全相同)。
-
每次需要一次性考虑每个分数段的同学,这是因为可能有很多成绩完全相同的同学报同一所大学,但录取一部分人就到达招生人数,这种情况下需要将所有学生招到该校(即使超过招生限额)。
代码
- C++
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 40010, M = 110, K = 5;
int n, m, k;
int cnt[M]; // 每所大学招生的人数
int wish[N]; // 记录每个人哪个志愿可以成功, wish[t]=w代表:大学t可以录取学生t
vector<int> uty[M]; // 记录大学录取的人
struct Person {
int id, ge, gi;
int wish[K]; // 该学生的志愿
int total() const {
return ge + gi;
}
bool operator< (const Person &t) const {
if (total() != t.total()) return total() > t.total();
return ge > t.ge;
}
bool operator== (const Person &t) const {
return ge == t.ge && gi == t.gi;
}
} p[N];
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < m; i++) scanf("%d", &cnt[i]);
for (int i = 0; i < n; i++) {
p[i].id = i;
scanf("%d%d", &p[i].ge, &p[i].gi);
for (int j = 0; j < k; j++)
scanf("%d", &p[i].wish[j]); // 第i个人的第j个志愿
}
sort(p, p + n);
memset(wish, -1, sizeof wish);
for (int i = 0; i < n; ) {
// 找到分数相同的所有人[i, j)
int j = i + 1;
while (j < n && p[i] == p[j]) j++;
// 一起考虑这些人可以分别被哪个志愿录取
for (int t = i; t < j; t++)
for (int u = 0; u < k; u++) {
int w = p[t].wish[u]; // 考虑第t个人的第u个志愿
if (cnt[w] > uty[w].size()) { // 说明大学w可以录取第t个人
wish[t] = w;
break;
}
}
// 录取
for (int t = i; t < j; t++)
if (wish[t] != -1)
uty[wish[t]].push_back(p[t].id);
i = j;
}
// 依次输出大学的录取结果
for (int i = 0; i < m; i++) {
if (uty[i].size()) {
sort(uty[i].begin(), uty[i].end());
printf("%d", uty[i][0]);
for (int j = 1; j < uty[i].size(); j++) printf(" %d", uty[i][j]);
}
puts("");
}
return 0;
}
AcWing 1569. 成绩单
问题描述
-
问题链接:AcWing 1569. 成绩单、原题链接
分析
-
将每个学生存入结构体中,然后根据降序排序。
-
之后从前遍历所有学生,判断其成绩是否在给定范围内,如果在给定范围内,输出即可。
代码
- C++
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int g1, g2;
struct Person {
string name, id;
int grade;
bool operator< (const Person &t) const {
return grade > t.grade;
}
} p[N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> p[i].name >> p[i].id >> p[i].grade;
sort(p, p + n);
cin >> g1 >> g2;
int cnt = 0;
for (int i = 0; i < n; i++)
if (p[i].grade >= g1 && p[i].grade <= g2) {
cout << p[i].name << ' ' << p[i].id << endl;
cnt++;
}
if (!cnt) puts("NONE");
return 0;
}
AcWing 1582. 买还是不买
问题描述
-
问题链接:AcWing 1582. 买还是不买、原题链接
分析
-
本题相当于给定我们一个目标串
b
,然后给定我们另一个串a
,问a
中是否完全包含b
(顺序无所谓)? -
可以使用一个哈希表
S
,首先统计a
中每个字符出现的次数,然后减去b
中每个字符出现的次数。之后遍历S
,如果S
中有字符出现次数为负数的话,说明a
不能完全包含b
。
代码
- C++
#include <iostream>
#include <unordered_map>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
unordered_map<char, int> S;
for (auto c : a) S[c]++;
for (auto c : b) S[c]--;
int sp = 0, sn = 0;
for (auto item : S)
if (item.second > 0) sp += item.second;
else sn -= item.second;
if (sn) printf("No %d\n", sn);
else printf("Yes %d\n", sp);
return 0;
}
AcWing 1585. 校园内的汽车
问题描述
-
问题链接:AcWing 1585. 校园内的汽车、原题链接
分析
-
每辆汽车都会对应很多事件,因此可以使用哈希表存储每辆车对应的事件。
-
之后对于每辆车将所有事件排序,并和合法事件保留,非法事件直接删除。
-
对于题目中的每个询问,我们可以将所有合法事件存放到一个容器
events
中,并按时间排序,因为询问时间是递增的,因此可以在上一次查询的基础上继续查询,只要当前考察的事件的时间小于查询时间,我们就考虑该事件,如果是车进入校园,则让车辆数sum
加一,否则减一。 -
对于在校停留时间最长的车辆,我们首先找出停留的最长时间
maxt
,然后找出所有停留时间等于maxt
的所有车辆,排序后输出即可。
代码
- C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
int n, m;
struct Event {
int tm, status; // tm:时间; status: 0表示in, 1表示out
bool operator< (const Event &t) {
return tm < t.tm;
}
};
// 返回汽车car在校内停留的总时间
int get(vector<Event> &car) {
int res = 0;
for (int i = 0; i < car.size(); i += 2)
res += car[i + 1].tm - car[i].tm;
return res;
}
int main() {
scanf("%d%d", &n, &m);
unordered_map<string, vector<Event>> cars;
char id[10], status[5];
for (int i = 0; i < n; i++) {
int hh, mm, ss;
scanf("%s %d:%d:%d %s", id, &hh, &mm, &ss, status);
int s = 0;
if (status[0] == 'o') s = 1;
cars[id].push_back({hh * 3600 + mm * 60 + ss, s});
}
vector<Event> events; // 所有合法事件
for (auto &item : cars) {
auto &ets = item.second;
sort(ets.begin(), ets.end());
int k = 0;
for (int i = 0; i < ets.size(); i++)
if (ets[i].status == 0) {
if (i + 1 < ets.size() && ets[i + 1].status == 1) { // in的下一个时间必须是out才合法
ets[k++] = ets[i];
ets[k++] = ets[i + 1];
i++;
}
}
ets.erase(ets.begin() + k, ets.end()); // 非法事件直接删除
for (int i = 0; i < ets.size(); i++) events.push_back(ets[i]);
}
// 求解每个时刻校园内停车的汽车总数
sort(events.begin(), events.end());
int k = 0, sum = 0; // sum: 当前在学校内的车的数量
while (m--) {
int hh, mm, ss;
scanf("%d:%d:%d", &hh, &mm, &ss);
int t = hh * 3600 + mm * 60 + ss;
while (k < events.size() && events[k].tm <= t) {
if (events[k].status == 0) sum++;
else sum--;
k++;
}
printf("%d\n", sum);
}
// 求解停车时间最长的汽车的车牌号,以及相应的时间长度
int maxt = 0;
for (auto &car : cars) maxt = max(maxt, get(car.second));
vector<string> res;
for (auto &item : cars)
if (get(item.second) == maxt)
res.push_back(item.first);
sort(res.begin(), res.end());
for (int i = 0; i < res.size(); i++) printf("%s ", res[i].c_str());
printf("%02d:%02d:%02d\n", maxt / 3600, maxt % 3600 / 60, maxt % 60);
return 0;
}
AcWing 1595. 螺旋矩阵
问题描述
-
问题链接:AcWing 1595. 螺旋矩阵、原题链接
分析
-
首先枚举出合法的行数列数
r、c
,然后将数组降序排序,最后将数据填到数组中即可。 -
螺旋数组需要使用到偏移量技巧。
代码
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 10010;
int n;
int w[N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> w[i];
sort(w, w + n, greater<int>());
int r, c;
for (int i = 1; i <= n / i; i++)
if (n % i == 0) {
r = n / i;
c = i;
}
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
vector<vector<int>> res(r, vector<int>(c, 0));
for (int i = 0, x = 0, y = 0, d = 1; i < n; i++) {
res[x][y] = w[i];
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= r || b < 0 || b >= c || res[a][b]) {
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;
}
for (int i = 0; i < r; i++) {
cout << res[i][0];
for (int j = 1; j < c; j++) cout << ' ' << res[i][j];
cout << endl;
}
return 0;
}
AcWing 1599. 合影
问题描述
-
问题链接:AcWing 1599. 合影、原题链接
分析
-
从最后一行开始输出,最后一行的元素可能多于前面的元素个数。
-
首先对所有的0人进行排序,然后按照题目要求进行模拟即可。
代码
- C++
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int n, m;
struct Person {
string name;
int h;
bool operator< (const Person &t) const {
if (h != t.h) return h > t.h;
return name < t.name;
}
} p[N];
string line[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> p[i].name >> p[i].h;
sort(p, p + n);
for (int i = 0, j = 0; i < m; i++) { // i枚举每一行,j枚举每个人
int len = n / m;
if (!i) len += n % m; // 特判最后一排
for (int r = len / 2 + 1, l = r - 1; r <= len || l > 0; r++, l--) {
if (r <= len) line[r] = p[j++].name;
if (l > 0) line[l] = p[j++].name;
}
cout << line[1];
for (int i = 2; i <= len; i++) cout << ' ' << line[i];
cout << endl;
}
return 0;
}
AcWing 1614. 单身狗
问题描述
-
问题链接:AcWing 1614. 单身狗、原题链接
分析
- 首先使用哈希表统计所有派对的人,然后遍历所有的伴侣,如果一对伴侣都在哈希表中存在,则将伴侣两个人都从哈希表删除,最后剩余的就是单身狗。
代码
- C++
#include <iostream>
#include <vector>
#include <unordered_set>
#include <algorithm>
using namespace std;
const int N = 50010, M = 10010;
int n, m;
struct Couple {
int a, b;
} c[N];
int ans[M];
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d%d", &c[i].a, &c[i].b);
scanf("%d", &m);
unordered_set<int> S;
for (int i = 0; i < m; i++) {
int x;
scanf("%d", &x);
S.insert(x);
}
for (int i = 0; i < n; i++) {
int a = c[i].a, b = c[i].b;
if (S.count(a) && S.count(b)) {
S.erase(a);
S.erase(b);
}
}
int k = 0;
for (auto x : S) ans[k++] = x;
sort(ans, ans + k);
printf("%d\n", k);
if (k) {
printf("%05d", ans[0]);
for (int i = 1; i < k; i++) printf(" %05d", ans[i]);
puts("");
}
return 0;
}
AcWing 1621. N 皇后问题
问题描述
-
问题链接:AcWing 1621. N 皇后问题、原题链接
分析
- 本题中给出了每列中皇后的位置,因此列中皇后不可能重复,需要判断行、主对角线方向(右上到左下)、副对角线是否存在两个皇后,可以使用数组
row、dg、udg
分别记录这些方向是否已经放置过皇后,从而判断是否存在矛盾即可。
代码
- C++
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010;
int n;
bool row[N], dg[N * 2], udg[N * 2];
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
memset(row, 0, sizeof row);
memset(dg, 0, sizeof dg);
memset(udg, 0, sizeof udg);
bool success = true;
for (int y = 1; y <= n; y++) {
int x;
scanf("%d", &x);
if (row[x] || dg[x + y] || udg[x - y + n]) success = false;
row[x] = dg[x + y] = udg[x - y + n] = true;
}
if (success) puts("YES");
else puts("NO");
}
return 0;
}
AcWing 1622. 推荐系统
问题描述
-
问题链接:AcWing 1622. 推荐系统、原题链接
分析
-
本题需要在用户访问过的物品中选出访问次数最多的
k
个物品进行输出。 -
因为
k
最大为10
,可以开辟一个长度为11
的数组top_k
记录出现次数最多的前k
个商品,然后输出。 -
当用户又访问一个商品时,如果这个商品不在
top_k
中 出现,则将其加到top_k
最后的位置(当top_k
存放的商品满了之后直接放到最后一个位置即可),然后重新排序输出出现最多的前k
个商品。如果这个商品在top_k
中,则排序后输出即可。
代码
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 50010;
int n, m;
int cnt[N]; // 统计每个商品被访问次数,用于排序
int top_k[11];
int main() {
scanf("%d%d", &n, &m);
int k = 0; // top_k中放入的物品数
for (int i = 0; i < n; i++) {
int id;
scanf("%d", &id);
if (i) {
printf("%d:", id);
for (int j = 0; j < k; j++) printf(" %d", top_k[j]);
puts("");
}
cnt[id]++;
bool exists = false;
for (int j = 0; j < k; j++)
if (top_k[j] == id) {
exists = true;
break;
}
if (!exists) top_k[k++] = id;
sort(top_k, top_k + k, [](int x, int y){
if (cnt[x] != cnt[y]) return cnt[x] > cnt[y];
return x < y;
});
k = min(k, m);
}
return 0;
}
AcWing 1625. 切整数
问题描述
-
问题链接:AcWing 1625. 切整数、原题链接
分析
-
直接按照题目要求进行验证即可。
-
一个小技巧:因为需要对整数切分,可以以字符串行书读入数据,然后切分后转成整数即可。
代码
- C++
#include <iostream>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
string s;
cin >> s;
int len = s.size() / 2;
int a = stoi(s.substr(0, len));
int b = stoi(s.substr(len));
int c = stoi(s);
if (a * b != 0 && c % (a * b) == 0) puts("Yes");
else puts("No");
}
return 0;
}
AcWing 1633. 外观数列
问题描述
-
问题链接:AcWing 1633. 外观数列、原题链接
分析
-
本题和Leetcode 0038 外观数列一样。
-
直接按照题目要求模拟一遍即可。
代码
- C++
#include <iostream>
using namespace std;
int main() {
int d, n;
cin >> d >> n;
string cur = to_string(d);
for (int k = 0; k < n - 1; k++) {
string next;
for (int i = 0; i < cur.size(); i++) {
int j = i + 1;
while (j < cur.size() && cur[j] == cur[i]) j++;
next += cur[i] + to_string(j - i);
i = j - 1;
}
cur = next;
}
cout << cur << endl;
return 0;
}
AcWing 1640. 堆
问题描述
-
问题链接:AcWing 1640. 堆、原题链接
分析
-
使用数组存储堆即可,堆顶元素在下标为
1
的位置。然后根据判断是否存在根节点大于子节点,或者子节点大于根节点。 -
如果上述两者都存在,说明不是堆,否则根据两者哪个成立确定是大顶堆还是小顶堆。
代码
- C++
#include <iostream>
using namespace std;
const int N = 1010;
int n;
int h[N]; // 存储堆
void dfs(int u) {
if (u * 2 <= n) dfs(u * 2);
if (u * 2 + 1 <= n) dfs(u * 2 + 1);
printf("%d", h[u]);
if (u != 1) cout << ' ';
}
int main() {
int T;
scanf("%d%d", &T, &n);
while (T--) {
for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
bool lt = false, gt = false;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 1; j++)
if (2 * i + j <= n) {
int a = h[i], b = h[2 * i + j]; // b是a的儿子
if (a > b) gt = true;
else lt = true;
}
if (lt && gt) puts("Not Heap");
else if (lt) puts("Min Heap");
else puts("Max Heap");
dfs(1); // 后续遍历
puts("");
}
return 0;
}