C2. Dual (Hard Version) Codeforces Round 889 (Div. 2) 1855C2

文章描述了一个编程竞赛中的问题,目标是通过不超过31次操作将一个长度为n(1≤n≤20)的数组变为非递减顺序。关键策略包括考虑全正数组的前缀和和全负数组的后缀和,以及通过翻倍最大值减少操作次数。作者提出了两种主要方案:使所有数为正或所有数为负,并分析了不同情况下的操作计数。
摘要由CSDN通过智能技术生成

Problem - C2 - Codeforces

题目大意:有一个长度为n的数组,每次操作可以选择两个数ai,aj,使ai+=aj,最多进行31次操作,要求将数组变成非递减的,问具体操作

1<=n<=20;-20<-ai<-20

思路:我们发现,对于一个全为正数的数组,它的前缀和一定是非递减的,对于一个全是负数的数组,它的后缀和一定是非递减的,而进行这样的操作最多需要19次,然后我们看都变成负或都变成正需要的操作,先看都变正,我们最优的操作肯定是让负数都去+上正数的最大值,这时我们能想到的极端情况就是负数有-20,正数只有1的时候,如果直接加要至少20次,但我们不如先令最大值1翻倍,最多翻5次,肯定会大于最小值的绝对值,然后所有负数只需一次操作即可,那么对应的极端情况就是7个-20,13个1,这时1翻倍需要5次,7个负数变正需要7次,求前缀和12次,正好31,当有8个-20,12个1时,如果仍然都变整数,就需要13次操作,但这时其实可以令每个整数都+上负数最小值,操作次数正好是12,所以我们令正数最大值大于负数最小值的绝对值的操作数x1+令负数最小值的绝对值大于整数最大值的操作数x2一定小于等于5,同时令所有正数变负的操作y1+令所有负数变正的操作数y2一定小于等于n=20,那么两个操作加起来<=25,而我们只取其中一种,一定小于等于12,所以,此法一定能在31次内完成要求

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int a[25];
int b[25];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		ll n;
		cin >> n;
		int ma = -21, mi = 21, mai, mii;
		int cnt1 = 0;
		int cnt2 = 0;
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
			if (a[i] > ma)
			{//找到最大值及其位置
				ma = a[i];
				mai = i;
			}
			if (a[i] < mi)
			{
				mi = a[i];
				mii = i;
			}
			b[i] = a[i];//复制一份数组
		}
		vector<pair<int, int>>out1, out2;
		int ans1 = 0, ans2 = 0;
		if (ma > 0)
		{//最大值大于0,才可以把其他数变成整数
			if (mi < 0)
			{//有负数
				while (a[mai] < -mi)
				{//翻倍,知道大于最小值的绝对值
					a[mai] *= 2;
					out1.push_back({ mai,mai });
				}
			}
			for (int i = 1; i <= n; i++)
			{//所有负数变正
				if (a[i] < 0)
				{
					out1.push_back({ i,mai });
				}
			}
			for (int i = 2; i <= n; i++)
			{//求前缀和
				out1.push_back({ i,i - 1 });
			}
			ans1 = out1.size();
		}
		else //因为我们最后直接去最小值,所以这里没进行的话,直接赋极大值
			ans1 = 1000;
		if (mi < 0)
		{//最小值小于0
			if (ma > 0)
			{//有正数
				while (-b[mii] < ma)
				{//翻倍
					b[mii] *= 2;
					out2.push_back({ mii,mii });
				}
			}
			for (int i = 1; i <= n; i++)
			{//证书变负
				if (b[i] > 0)
				{
					out2.push_back({ i,mii });
				}
			}
			for (int i = n - 1; i >= 1; i--)
			{//求后缀和
				out2.push_back({ i,i + 1 });
			}
			ans2 = out2.size();
		}
		else
			ans2 = 1000;
		if (min(ans1, ans2) == 1000)
		{//数组里全0
			cout << 0 << endl;
			continue;
		}
		if (ans1 < ans2)
		{//一定有一个<=31
			cout << ans1 << endl;
			for (int i = 0; i < ans1; i++)
			{
				cout << out1[i].first << " " << out1[i].second << endl;
			}
		}
		else
		{
			cout << ans2 << endl;
			for (int i = 0; i < ans2; i++)
			{
				cout << out2[i].first << " " << out2[i].second << endl;
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值