D. Slime Escape codeforces 1734D

Problem - 1734D - Codeforces

题目大意:有n个可正可负的整数ai,从第k个数开始(第k个数为非负数)可以选择向右走一个单位或者向左走一个单位,然后第k个数+上那个数,依次循环,直到走到1或n的位置时和不小于0算作胜利,或者在中途和小于0时即失败

1<=n<=2e5,-1e9<=ai<=1e9

思路1:我们先假定一个最终要取胜的方向,比如我们选定最后走到最左边i=1的位置出去,我们在每一步向左走之前,先看看向右走有没有收益,也就是看在我们生命值不小于0的前提下,向右边走几步能否让当前生命值更大,如果往右走没有收益的话,那这时我们肯定要往左走,每走一步都检查一下往右走有没有收益,这样就考虑到了所有情况,然后再假设从右边出去,进行一次类似的操作即可,如果两边都出不去就是NO

#include
#include
#include
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
ll a[N];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, k;
		scanf("%d%d", &n, &k);
		
		bool flag = 1;
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld", &a[i]);			
		}
		queue>pos;
		ll mi = 0;//求出连续的负数的和,为取得收益前需要的代价
        ll sum = 0;//求出收益
		for (int i = k - 1; i >= 1; i--)
		{//假设我们要从右边出,先求出往左走的收益
			sum += a[i];//求和
			mi = min(mi, sum);//维护负数和
			if (sum > 0)
			{//到当前收益为正
				pos.push(make_pair(sum,mi));//记录收益和要取得收益需要付出的代价
				sum = mi = 0;//重新统计收益和代价
			}
		}
		sum = a[k];
		for (int i = k+1 ; i <= n; i++)
		{//向右走看能不能出
			while (pos.size() && sum + pos.front().second >= 0)
			{//每一步前先看看能不能付得起向左的代价
				sum += pos.front().first;//能的话就先取得向左走的收益
				pos.pop();
			}
			sum += a[i];//向右走,维护当前生命值
			if (sum < 0)
			{//生命值<0,从右边不能出
				flag = 0;
				break;
			}				
		}
		if (flag)
		{
			printf("YES\n");
			continue;
		}
		queue>pos2;
		sum = mi = 0;
		for (int i = k+1 ; i <= n; i++)
		{//假设从左边出,记录右边的收益
			sum += a[i];
			mi = min(mi, sum);
			if (sum > 0)
			{
				pos2.push(make_pair(sum, mi));
				sum = mi = 0;
			}
		}
		flag = 1;
		sum = a[k];
		for (int i = k-1 ; i >= 1; i--)
		{//向左走
			while (pos2.size() && sum + pos2.front().second >= 0)
			{
				sum += pos2.front().first;
				pos2.pop();
			}
			sum += a[i];
			if (sum < 0)
			{
				flag = 0;
				break;
			}
		}
		if (flag)
		{
			printf("YES\n");
			continue;
		}
		printf("NO\n");
	}
	return 0;
}

简化思路:我们用对组数组事先统计出向左走和向右走的代价和收益,然后走的时候那边代价能付得起就往那边走,省去了两遍重复操作

#include
#include
#include
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
ll a[N];
paircol[N], cor[N];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, k;
		scanf("%d%d", &n, &k);
		
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld", &a[i]);
		}
		ll mi = 0;//求连续负数和代价
		int cnt = 0;//储存左边收益的数组的容量
        ll sum = 0;//当前累加和
		bool flag = 1;//生命值不小于0的标记
		for (int i = k - 1; i >= 1; i--)
		{//求左边的收益和代价
			sum += a[i];//求累加和
			mi = min(mi, sum);//求代价
			if (sum > 0||i==1)
			{//注意如果到最左端收益<0也要记录
				col[++cnt] = make_pair(sum, mi);//记录左边的收益,代价
				sum = mi = 0;重置累加和和代价
			}
		}
		sum = mi = 0;
		int cnt2 = 0;
		for (int i = k + 1; i <= n; i++)
		{//求右边的收益和代价
			sum += a[i];
			mi = min(mi, sum);
			if (sum > 0 || i == n)
			{
				cor[++cnt2] = make_pair(sum, mi);
				sum = mi = 0;
			}
			
		}
		sum = a[k];
		int nowl = 0, nowr = 0;//遍历左收益和右收益的指针
		while (nowl < cnt && nowr < cnt2)
		{//如果一边的指针和数组容量相同,说明走出去了
			bool temp1 = 0, temp2 = 0;//标记左和右能不能走
			while (nowl != cnt && sum + col[nowl + 1].second >= 0)
			{//如果左边的代价+当前生命不小于0,就往左走
				sum += col[++nowl].first;//维护当前生命值
				temp1 = 1;
			}
			while(nowr!=cnt2&&sum+cor[nowr+1].second>=0)
			{//如果右边的代价+当前生命不小于0,就往右走
				sum += cor[++nowr].first;
				temp2 = 1;
			}
			if (!temp1 && !temp2)
			{//左右都没有走,说明走不出去
				flag = 0;
				break;
			}
		}
		if (nowl==cnt||nowr==cnt2)
		{//如果一边的指针和数组容量相同,说明走出去了
			printf("YES\n");
			continue;
		}
		printf("NO\n");
	}
	return 0;
}

你可能感兴趣的:(贪心,c++,算法)