机器人开箱子【贪心 + 模拟】

题目描述

多多最近测试一款自动用钥匙打开箱子的机器人。每个箱子打开需要的钥匙数量都不一样,当流水线上的箱子经过机器人时,只要机器人手里的钥匙数量足够,箱子就会被机器人自动打开。

每个箱子里面都会有 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;
}

相关推荐
伟大的车尔尼3 天前
贪心题目:使绳子变成彩色的最短时间
贪心
Tisfy5 天前
LeetCode 0678.有效的括号字符串:O(n)+O(1)一次遍历
算法·leetcode·字符串·题解·贪心·括号匹配
伟大的车尔尼6 天前
贪心题目:坏了的计算器
贪心
lvwangshu8 天前
T1 称重(weight)题解
题解·贪心·总结·技巧·性质题·思维好题
伟大的车尔尼8 天前
贪心题目:十-二进制数的最少数目
贪心
伟大的车尔尼10 天前
贪心题目:不含 AAA 或 BBB 的字符串
贪心
伟大的车尔尼11 天前
贪心题目:最少的后缀翻转次数
贪心
lvwangshu13 天前
P12960 毒药 题解
动态规划·题解·贪心·技巧·性质题·思维好题
伟大的车尔尼21 天前
贪心题目:优势洗牌
贪心