题目描述
多多最近测试一款自动用钥匙打开箱子的机器人。每个箱子打开需要的钥匙数量都不一样,当流水线上的箱子经过机器人时,只要机器人手里的钥匙数量足够,箱子就会被机器人自动打开。
每个箱子里面都会有 1 把钥匙,打开箱子后,机器人就会多 1 把钥匙。
传送带上从左到右排列着 n 个箱子,第 i 个箱子需要至少 a[i] 把钥匙才能打开。机器人初始位于第一个箱子处,首先按照从左到右的顺序依次检查箱子。机器人可以改变扫描方向,使之前因钥匙不足而未打开的箱子再次被检查。
机器人可以随时改变传送带方向,使之前因为钥匙不足而没有打开的箱子再次经过机器人。已经打开过的箱子不会重复获得钥匙。
机器人初始没有钥匙,因此一开始只能打开要求钥匙数为 0 的箱子。
求:如果要打开所有箱子,最少需要改变传送带方向多少次;如果无论如何都无法打开所有箱子,输出 -1。
输入描述
第一行输入一个整数 T,表示测试用例组数:
text
1 <= T <= 10
对于每组测试用例:
第一行输入整数 n,表示箱子数量:
text
1 <= n <= 500
第二行输入 n 个整数 a[1], a[2], ..., a[n]:
text
0 <= a[i] < n
输出描述
对于每组测试用例输出一个整数,表示打开所有箱子所需的最少换向次数。
如果无法打开所有箱子,输出:
text
-1
样例
输入
text
3
2
0 1
1
1
3
0 2 1
输出
text
0
-1
1
C++ 代码
cpp
// 题目有一些隐含的条件:
// 1. 打开箱子后,钥匙不会消耗
// 2. 如果某个箱子打不开,可以跳过去处理下一个箱子
// 思路:贪心 + 双向模拟
// 题目让我们找的是最少改变传送带方向的次数
// 那么在第一个箱子处转向和在最后一个箱子处转向对答案的影响是一样的
// 那么我们优先从最后一个箱子处转向,这样钥匙数量只多不少,打开的箱子数也只多不少
// 每轮完整扫描需要 O(N) 时间。
// 只要还没有结束且本轮打开了箱子,已经打开的箱子数量就至少增加 1。
// 因此最多执行 O(N) 轮扫描。时间复杂度:O(N^2)
// 空间复杂度:O(N)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 510;
int n;
int w[N];
bool vis[N];
void solve() {
int keys = 0;
int opened = 0;
int switch_cnt = 0;
int direct = 0; // 0: right, 1: left
while(true) {
int new_opened = opened;
if(direct == 0) {
for(int i = 0; i < n; i ++ ) {
if(!vis[i] && keys >= w[i]) {
++ keys;
++ new_opened;
vis[i] = true;
}
}
}
else {
for(int i = n - 1; i >= 0; i -- ) {
if(!vis[i] && keys >= w[i]) {
++ keys;
++ new_opened;
vis[i] = true;
}
}
}
if(new_opened == n) {
cout << switch_cnt << endl;
break;
}
else if(new_opened == opened) {
cout << -1 << endl;
break;
}
opened = new_opened;
direct ^= 1;
++ switch_cnt;
}
}
int main()
{
int T;
cin >> T;
while(T -- ) {
memset(vis, false, sizeof vis);
cin >> n;
for(int i = 0; i < n; i ++ ) cin >> w[i];
solve();
}
return 0;
}