题目笔记——AT_abc146_f [ABC146F] Sugoroku
其实也可以不用单调队列就做出来了。
思路
本题中要维护两个最小值:回合最小和字典序最小。
- 先看回合最小,我们只需要在每次前进时走到能往前走的最远下标,就可以求出回合最小。
- 但这样的话所得到的字典序就会最大,因为只要能从起点走到终点,就可以从终点原路返回到起点,而且回合数一样,于是想到从后往前遍历,也是每次前进时走到能往前走的最远下标,这样既维护了回合最小值,又保证了字典序最小。
1. 预处理
先从后往前预处理出每个点及它以后第一个不为“GameOver 格”的下标,以便查询,复杂度为 \(O(n)\)。
2. 遍历
判断如果能走到的所有地方都为“GameOver 格”,则说明 \(1\) 到 \(n\) 之间走不通,否则把 \(i\) 更新为往前走所能走到的最远下标。
3. 输出
由于是倒序遍历,所以可以用栈暂时存上答案的下标,结束后在输出下标的差就好了。
本蒟蒻的第一篇题解,大佬勿喷。
Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 20;
int n, m, cnt, ans, num[N];
string s;
stack<int> stk;
signed main(){
cin >> n >> m >> s;
num[n] = n;
for(int i = n - 1; i >= 0; i--){
if(s[i] == '1') num[i] = num[i + 1];
else num[i] = i;
}
for(int i = n; i > 0; ){
if(i == num[max(0ll, i - m)]){
cout << -1;
return 0;
}
stk.push(i);
i = num[max(0ll, i - m)];
}
while(!stk.empty()){
cout << stk.top() - ans << ' ';
ans = stk.top();
stk.pop();
}
return 0;
}

浙公网安备 33010602011771号