题目描述
题意
在坐标轴上, 有一个区域; 给定一个在这个区域中的初始坐标和初始血量, 要求在血量始终大于等于0的前提下, 走出区域(走到左端或右端). 每个整数坐标点有一个值, 经过遇到后会改变血量.
思路
分别考虑最终从左侧和从右侧通过的两种情况
以从右侧通过为例:
- 从起始位置向左侧走, 进行预处理. 得到每段正数补给的值和得到这个补给过程中血量的最低值.(放到队列中)
- 预处理过后, 从起始位置向右走, 每到一个新的坐标点, 检查队首的补给是否可以吃下(拿补给过程中最低血量是否大于等于0). 如果可以吃下, 就立即使用这个补给.
code
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int N = 2e5 + 10;
ll a[N];
int main()
{
int T;
cin >> T;
while(T --)
{
int n, k;
cin >> n >> k;
a[0] = a[n + 1] = 0;
for(int i = 1; i <= n; i ++) cin >> a[i];
// 假设从右侧通过
queue<PII> q;
ll inc = 0;
ll inc_min = 0;
for(int i = k - 1; i > 0; i --)
{
inc += a[i];
inc_min = min(inc_min, inc);
if(inc > 0)
{
q.push({inc_min, inc});
inc = 0;
inc_min = 0;
}
}
ll me = a[k];
for(int i = k + 1; i <= n + 1; i ++)
{
while(q.size() && q.front().first + me >= 0)
{
auto t = q.front(); q.pop();
me += t.second;
}
me += a[i];
if(me < 0) break;
}
if(me >= 0)
{
cout << "YES\n";
continue;
}
// 假设从左侧通过
q = queue<PII> ();
inc = 0;
inc_min = 0;
for(int i = k + 1; i <= n; i ++)
{
inc += a[i];
inc_min = min(inc_min, inc);
if(inc > 0)
{
q.push({inc_min, inc});
inc = 0;
inc_min = 0;
}
}
me = a[k];
for(int i = k - 1; i >= 0; i --)
{
while(q.size() && q.front().first + me >= 0)
{
auto t = q.front(); q.pop();
me += t.second;
}
me += a[i];
if(me < 0) break;
}
if(me >= 0)
{
cout << "YES\n";
}
else cout << "NO\n";
}
return 0;
}
注意与总结
将从左侧和右侧通过分开考虑, 有助于思考.