C2. Dual (Hard Version)(思维)

给定一个整数数组,目标是在最多31次操作中使数组变得非递减。可以通过将元素加到前面或后面的元素上来实现。文章描述了如何处理正数、负数和零的情况,以及当正数和负数数量较大时的策略。最终代码实现了这一逻辑。
摘要由CSDN通过智能技术生成

传送门

题目大意:

给你一个整数数组 a 1 , a 2 , … , a n a_1, a_2,\dots, a_n a1,a2,,an(正数、负数或 0 0 0)。你可以对数组进行多种运算(可能有 0 0 0种运算)。

在一次操作中,您可以选择 i , j i, j i,j 1 ≤ i , j ≤ n 1 \leq i, j \leq n 1i,jn,它们可以相等)并设置 a i : = a i + a j a_i := a_i + a_j ai:=ai+aj(即将 a j a_j aj加到 a i a_i ai)。

使数组在最多 31 31 31次运算中不递减(即 a i ≤ a i + 1 a_i \leq a_{i+1} aiai+1次为 1 ≤ i ≤ n − 1 1 \leq i \leq n-1 1in1次)。您不需要尽量减少运算次数。
输入

每个测试包含多个测试用例。第一行包含测试用例的数量 t t t 1 ≤ t ≤ 500 1 \le t \le 500 1t500)。测试用例说明如下。

第一行包含一个整数 n n n 1 ≤ n ≤ 20 1 \le n \le 20 1n20)–数组的长度。

第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an ( − 20 ≤ a i ≤ 20 -20 \le a_i \le 20 20ai20)–执行操作前的数组。

输出

对于每个测试用例,请按以下格式输出您的操作。

第一行应包含一个整数 k k k ( 0 ≤ k ≤ 31 0 \le k \le 31 0k31)–操作次数。

接下来的 k k k行依次表示 k k k个操作。每一行 k k k都应包含两个整数 i i i j j j 1 ≤ i , j ≤ n 1 \leq i, j \leq n 1i,jn)–相应的操作是将 a j a_j aj加到 a i a_i ai

在所有操作之后,数组 a 1 , a 2 , … , a n a_1, a_2,\dots, a_n a1,a2,,an 必须是非递减的。

思路:

     首先,我们先考虑数组全为非负数或非正数的情况,如果数组全为非负数,我们从前往后遍历可以使 a i = a i + a i − 1 a_i = a_i + a_{i-1} ai=ai+ai1,同理,如果数组全为非正数,那么就从后往前遍历,使得 a i = a i + a i + 1 a_i = a_i + a_{i+1} ai=ai+ai+1,这么的话最多操作19次就可以使得数组非递减.
     然后,我们在考虑其他情况,如果上述情况不满足的话,我们想一想能不能通过剩下的 31 − 19 = 12 31-19=12 3119=12次操作将数组的全部元素变成非正数或非负数,答案是可以的.

     记 c n t 1 cnt1 cnt1为正数的个数,记 c n t 2 cnt2 cnt2为负数的个数,这样的话又可以分为两种情况.

  1. m a x ( c n t 1 , c n t 2 ) ≤ 12 max(cnt1,cnt2)\leq12 max(cnt1,cnt2)12,在这种情况下,我们可以将数组中的元素通过最多12次操作就可以把它们变成同一个符号,然后在执行上述的19次的递推操作,就可以使得数组非递减.
  2. m a x ( c n t 1 , c n t 2 ) > 12 max(cnt1,cnt2)>12 max(cnt1,cnt2)>12,如果 c n t 1 > c n t 2 cnt1>cnt2 cnt1>cnt2,则将一个整数自增5次,比如 1 → 2 → 4 → 8 → 16 → 32 1\rightarrow2\rightarrow4\rightarrow8\rightarrow16\rightarrow32 12481632,就可以将 c n t 2 cnt2 cnt2个负数全部变成正数, c n t 2 cnt2 cnt2最多有7个,然后在执行上述的19次的递推操作,.如果 c n t 2 > c n t 1 cnt2>cnt1 cnt2>cnt1,也是这个道理.最多可以经过 5 + 7 + 19 = 31 5+7+19=31 5+7+19=31次操作将他们变成非递减数组.

完整代码:

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define int long long
const int N = 2e5 + 10, mod = 1e9 + 7;
typedef pair<int, int> PII;
typedef long long ll;
const int INF_MAX = 0x3f3f3f3f3f3f3f3f;
int a[N];
void solve()
{
    int n;
    cin >> n;
    int cnt1 = 0, cnt2 = 0;//cnt1正数数量,cnt2负数数量
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] > 0)cnt1++;
        else if (a[i] < 0)cnt2++;//统计正负数数量
    }
    vector<PII>ans;
    int mx = *max_element(a + 1, a + 1 + n);//最大值
    int mi = *min_element(a + 1, a + 1 + n);//最小值
    int pos_mx = max_element(a + 1, a + 1 + n) - a;//最大值坐标
    int pos_mi = min_element(a + 1, a + 1 + n) - a;//最小值坐标
    if (max(cnt1, cnt2) <= 12)
    {
        if (mi == 0 && mx == 0)//数组全为0的情况
        {

        }
        else if (mx < 0)//数组中最大值为负数的情况
        {
            for (int i = n; i >= 2; i--)
                ans.push_back({ i - 1,i });
        }
        else if (mi > 0)//数组中最小值为正数的情况
        {
            for (int i = 2; i <= n; i++)
                ans.push_back({ i,i - 1 });
        }
        else
        {
            if (abs(mx) >= abs(mi))//将数组中的负数全部变成正数
            {
                for (int i = 1; i <= n; i++)
                    if (a[i] < 0)
                        ans.push_back({ i,pos_mx });
                for (int i = 2; i <= n; i++)
                    ans.push_back({ i,i - 1 });
            }
            else//将数组中的正数全部变成负数
            {
                for (int i = 1; i <= n; i++)
                    if (a[i] > 0)
                        ans.push_back({ i,pos_mi });
                for (int i = n; i >= 2; i--)
                    ans.push_back({ i - 1,i });
            }

        }  
    }
    else
    {
        if (cnt1 < cnt2)//如果正数的个数小于负数的个数
        {
            int num = 5;
            while (num--)ans.push_back({ pos_mi,pos_mi });
            for (int i = 1; i <= n; i++)
                if (a[i] > 0)
                    ans.push_back({ i,pos_mi });
            for (int i = n; i >= 2; i--)
                ans.push_back({ i - 1,i });
        }
        else//如果负数的个数小于正数的个数
        {
            int num = 5;
            while (num--)ans.push_back({ pos_mx,pos_mx });
            for (int i = 1; i <= n; i++)
                if (a[i] < 0)
                    ans.push_back({ i,pos_mx });
            for (int i = 2; i <= n; i++)
                ans.push_back({ i,i - 1 });
        }
    }
    cout << ans.size() << '\n';
    for (auto i : ans)
        cout << i.first << " " << i.second << '\n';
}
signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--)solve();
    return 0;
}

最后感谢JustACommonMan大佬的思路点拨.🌹🌹🌹

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值