深海潜艇探险
题意
一艘潜艇初始能量为 ,要穿越
个危险区域。第
个区域穿越时消耗
能量,穿越后补充
能量。驾驶员可以自由选择穿越顺序,但全程能量必须严格大于 0。问是否存在一种顺序能安全通过所有区域。
思路
自由选择顺序——典型的贪心调度问题。关键是怎么排序才能让能量尽可能"安全"。
先想一个直觉: 如果某个区域"赚能量"(),那当然越早做越好,这样后面的能量越充裕。反过来,如果某个区域"亏能量"(
),应该放到最后做,趁能量高的时候硬扛。
所以第一步很自然:把区域分成两组,一组是"收益区"(),一组是"消耗区"(
)。收益区先做,消耗区后做。
收益区内部怎么排? 都是赚能量的区域,做完之后能量只增不减。瓶颈在于穿越时消耗的 ——万一当前能量不够
就挂了。所以
小的先做,风险最低。
消耗区内部怎么排? 这组做完能量一定会下降。考虑两个区域 A 和 B,先做 A 再做 B 的条件是:
$$
先做 B 再做 A 的条件是:
$$
两种方案对总能量的最终影响一样(都减去 ),区别在于中间的最低点。可以证明,按
从大到小排序是最优的——
大的先做,做完能补回更多能量,给后面留更多余量。
为什么按 降序? 直觉上,消耗区每做一个能量就会净减少,你希望前面做的那些能"回血"多一点,把后面的能量底线抬高。如果把回血少的放前面,后面的起点更低,更容易崩。
复杂度
- 时间:
,排序为主
- 空间:
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int T;
scanf("%d",&T);
while(T--){
long long n, E;
scanf("%lld%lld",&n,&E);
vector<pair<long long,long long>> gain, lose;
for(int i=0;i<n;i++){
long long c,g;
scanf("%lld%lld",&c,&g);
if(g >= c) gain.push_back({c,g});
else lose.push_back({c,g});
}
sort(gain.begin(), gain.end(), [](auto&a, auto&b){
return a.first < b.first;
});
sort(lose.begin(), lose.end(), [](auto&a, auto&b){
return a.second > b.second;
});
bool ok = true;
long long cur = E;
for(auto&[c,g]:gain){
if(cur <= c){ ok=false; break; }
cur = cur - c + g;
}
if(ok){
for(auto&[c,g]:lose){
if(cur <= c){ ok=false; break; }
cur = cur - c + g;
}
}
printf("%s\n", ok?"Yes":"No");
}
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine().trim());
StringBuilder sb = new StringBuilder();
while (T-- > 0) {
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
long E = Long.parseLong(st.nextToken());
List<long[]> gain = new ArrayList<>();
List<long[]> lose = new ArrayList<>();
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
long c = Long.parseLong(st.nextToken());
long g = Long.parseLong(st.nextToken());
if (g >= c) gain.add(new long[]{c, g});
else lose.add(new long[]{c, g});
}
gain.sort((a, b) -> Long.compare(a[0], b[0]));
lose.sort((a, b) -> Long.compare(b[1], a[1]));
long cur = E;
boolean ok = true;
for (long[] z : gain) {
if (cur <= z[0]) { ok = false; break; }
cur = cur - z[0] + z[1];
}
if (ok) {
for (long[] z : lose) {
if (cur <= z[0]) { ok = false; break; }
cur = cur - z[0] + z[1];
}
}
sb.append(ok ? "Yes" : "No").append('\n');
}
System.out.print(sb);
}
}
import sys
input = sys.stdin.readline
T = int(input())
for _ in range(T):
n, E = map(int, input().split())
gain = []
lose = []
for i in range(n):
c, g = map(int, input().split())
if g >= c:
gain.append((c, g))
else:
lose.append((c, g))
gain.sort(key=lambda x: x[0])
lose.sort(key=lambda x: -x[1])
cur = E
ok = True
for c, g in gain:
if cur <= c:
ok = False
break
cur = cur - c + g
if ok:
for c, g in lose:
if cur <= c:
ok = False
break
cur = cur - c + g
print("Yes" if ok else "No")
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
let idx = 0;
let T = parseInt(lines[idx++]);
const res = [];
while (T-- > 0) {
const [n, E] = lines[idx++].split(' ').map(Number);
const gain = [], lose = [];
for (let i = 0; i < n; i++) {
const [c, g] = lines[idx++].split(' ').map(Number);
if (g >= c) gain.push([c, g]);
else lose.push([c, g]);
}
gain.sort((a, b) => a[0] - b[0]);
lose.sort((a, b) => b[1] - a[1]);
let cur = E;
let ok = true;
for (const [c, g] of gain) {
if (cur <= c) { ok = false; break; }
cur = cur - c + g;
}
if (ok) {
for (const [c, g] of lose) {
if (cur <= c) { ok = false; break; }
cur = cur - c + g;
}
}
res.push(ok ? "Yes" : "No");
}
console.log(res.join('\n'));
});

京公网安备 11010502036488号