2023年 8月18日普及组南外集训题解
A 陷阱
我们可以从 \(l\) 枚举到 \(d\),再计算是否满足要求,满足要求加入到数组中,输出第一个和最后一个
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int k;
int nums[N];
int main() {
int l, d, x;
cin >> l >> d >> x;
for (int i = l; i <= d; i++) {
int num = i;
int sum = 0;
while (num > 0) {
sum += num % 10;
num /= 10;
}
if (sum == x)
nums[k++] = i;
}
cout << nums[0] << endl;
cout << nums[k - 1];
return 0;
}
B 翻转元素
我们可以使用动态规划来解决这个问题,\(f[i]\) 表示前 \(i\) 个数能达到的最大值,那么我们再加一维表示是否翻转
\(f[i][0]\) 表示没有翻转,如果前面一个数没有翻转,直接加 \(x\),如果翻转了加 \(-x\)
\(f[i][1]\) 表示翻转,如果前面一个数没有翻转,那么自己翻自己加 \(-x\),否则加 \(x\)
初始化:\(f[i][0]=x\) 没有翻转 \(f[i][1]=-x\) 翻转了
由于最少两个数翻转,那么最后的答案只能是不翻转的 \(f[n][0]\)
#include <iostream>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int f[N][2];
signed main() {
int n;
cin >> n;
int x;
cin >> x;
f[1][0] = x;
f[1][1] = -x;
for (int i = 2; i <= n; i++) {
cin >> x;
f[i][0] = max(f[i - 1][0] + x, f[i - 1][1] - x);
f[i][1] = max(f[i - 1][0] - x, f[i - 1][1] + x);
}
cout << f[n][0];
return 0;
}
C 移动棋子
我们可以发现,想让移动的步数最小,就要让空隙最小。我们干脆把所有空隙大的放一个棋子不去移动,然后只用一个棋子移动。
为了避免负数这种麻烦的情况,我们直接对原数组排序,然后用一个数组计算所有的空隙,从大到小排序,然后选出 \(n-1\) 个空隙最大的摆上棋子,其余的加起来就行了
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int x[N], nums[N];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) cin >> x[i];
sort(x + 1, x + m + 1);
int res = 0;
for (int i = 1; i < m; i++) {
res += x[i + 1] - x[i];
nums[i] = x[i + 1] - x[i];
}
sort(nums + 1, nums + m, greater<int>());
for (int i = 1; i < n; i++) res -= nums[i];
printf("%d", res);
return 0;
}
D 插队
回忆一下,我们能单调栈解决的问题是找前面第一个比当前大的元素
而实际上我们也可以把它推广到这一题
考虑每一个被作为“前面第一个比当前大”的点,以它为最大值的点肯定是一个单调递增的序列
如以1为“前面第一个比当前大”的有2,3,4,构成一个单调的序列,而5自己也是一个递增序列
那么我们将2,3,4”挂“到节点1上,那么比2小的数肯定会比3小——我们可以再维护一个单调栈,维护当前”前面第一个比当前大“前面的元素,那么我们每次从中取最后一个较大的就是答案
#include <iostream>
#include <vector>
using namespace std;
const int N = 1000005;
int stk[N], res[N], h[N];
int tt;
vector<int> vec[N];
int main() {
int n;
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &h[i]);
while (tt && h[i] > h[stk[tt]]) tt--;
vec[stk[tt]].push_back(i);
stk[++tt] = i;
}
tt = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < vec[i].size(); j++) {
int k = vec[i][j];
while (tt && h[k] > h[stk[tt]]) tt--;
res[k] = stk[tt];
}
stk[++tt] = i;
}
for (int i = 1; i <= n; i++) {
int ans = res[i] + 1;
printf("%lld\n", ans);
}
return 0;
}
E 小熊
我们可以使用宽搜来拓展出每个点位蜜蜂要多长时间到,然后再二分猜小熊可以停留在初始位置吃蜂蜜的最长时间
然后再使用一次宽搜进行检测是否可行
记得要初始化!!!
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 805;
char mp[N][N]; //地图
int lim[N][N]; //每个位置蜜蜂到的时间
bool st[N][N]; //是否走过
int sx, sy; //起点位置
int n, s;
struct node {
int x, y, u;
};
queue<node> q;
int dx[4] = { -1, 0, 1, 0 }, dy[4] = { 0, 1, 0, -1 };
//预处理出每个点位蜜蜂要多久到达
void bfs() {
while (!q.empty()) {
node t = q.front();
q.pop();
lim[t.x][t.y] = t.u; //将步数加进去
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
// 1.是否走过 2.是否出界 3.是否是森林或小熊的老家
if (st[x][y] || x < 1 || x > n || y < 1 || y > n || mp[x][y] == 'T' || mp[x][y] == 'D')
continue;
st[x][y] = true; //打上标记
q.push({ x, y, t.u + 1 });
}
}
}
bool check(int minute) {
//上来就被抓住了
if (minute >= lim[sx][sy])
return false;
q = queue<node>(); //清空队列再次使用
q.push({ sx, sy, minute * s });
memset(st, false, sizeof st); //每次拓展需要初始化
st[sx][sy] = true;
while (!q.empty()) {
node t = q.front();
q.pop();
//到家了
if (mp[t.x][t.y] == 'D')
return true;
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
// 1.是否走过 2.是否出界 3.是否是草地 4.时间是否够(不会被抓住)5.是否走过
if (x < 1 || x > n || y < 1 || y > n || (t.u + 1) / s >= lim[x][y] || mp[x][y] == 'T' || st[x][y])
continue;
st[x][y] = true;
q.push({ x, y, t.u + 1 });
}
}
return false;
}
int main() {
memset(lim, 0x3f, sizeof lim); //有些地方扩展不到
scanf("%d%d", &n, &s);
for (int i = 1; i <= n; i++) {
scanf("%s", mp[i] + 1);
for (int j = 1; j <= n; j++) {
if (mp[i][j] == 'H')
q.push({ i, j, 0 }), st[i][j] = true; //记录蜂巢的位置
if (mp[i][j] == 'M')
sx = i, sy = j; //记录起点
}
}
bfs();
int l = 0, r = n * n; //二分猜时间
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid))
l = mid;
else
r = mid - 1;
}
if (check(l))
printf("%d", l); //是否无解
else
printf("-1");
return 0;
}