k次取反后最大化的数组和(分类讨论/贪心)

 

 说实话,该题我在做的时候耗费了大量的时间,我的思路改了又改,调试调了又调,可算是调了出来,下面是我写的代码(不过是把所有可能的情况列了出来),代码很冗长

由于我们希望数组的和尽可能大,因此除非万不得已,我们应当总是修改负数,并且优先修改值最小的负数。因为将负数 -x修改成 x 会使得数组的和增加 2x,所以这样的贪心操作是最优的

  • 当给定的 K 小于等于数组中负数的个数时,我们按照上述方法从小到大依次修改每一个负数即可。但如果 K 的值较大,那么我们不得不去修改非负数(即正数或者 0)了。由于修改 0 对数组的和不会有影响,而修改正数会使得数组的和减小,因此:
  • 如果数组中存在 0,那么我们可以对它进行多次修改,直到把剩余的修改次数用完;
  • 如果数组中不存在 0 并且剩余的修改次数是偶数,由于对同一个数修改两次等价于不进行修改,因此我们也可以在不减小数组的和的前提下,把修改次数用完;
  • 如果数组中不存在 0 并且剩余的修改次数是奇数,那么我们必然需要使用单独的一次修改将一个正数变为负数(剩余的修改次数为偶数,就不会减小数组的和)。为了使得数组的和尽可能大,我们就选择那个最小的正数

需要注意的是,在之前将负数修改为正数的过程中,可能出现了(相较于原始数组中最小的正数)更小的正数,这一点不能忽略

class Solution {
public:


int largestSumAfterKNegations(vector<int>& nums, int k) {
	sort(nums.begin(), nums.end());//先排序
    int cn_fu=0,sign_0=0;

    for(int i=0;i<nums.size();i++)
    {
        if(nums[i]==0)
        {
            sign_0=1;//判断有没有0
        }
        if(nums[i]<0)//计算负数的个数
        {
            cn_fu++;
        }
    }
    int check=k-cn_fu;//将负数都变成正的之后还有多少次反转操作
    int minnum=1000;
    if(k<=cn_fu)//如果操作次数k小于等于负数个数,直接全部变成正的即可
    {
    	for (int i = 0; i <cn_fu; i++)
	    {			
		    nums[i] = -nums[i];
		    k--;
            if(k==0)
            {
                break;
            }
	    }        
    }
    else
    {
        if(sign_0)//如果有0,就先将负数变正之后k清0(因为全部用在0上)
        {
        	for (int i = 0; i <cn_fu; i++)
	        {			
		      nums[i] = -nums[i];
		      k--;
	        }
            k=0;           
        }
        else//没有0
        {
            if(check%2==0)//剩余操作数是偶数,负数变正之后,k清0
            {
            	for (int i = 0; i <cn_fu; i++)
	            {			
		          nums[i] = -nums[i];
		          k--;
                  if(k==check)
                  {
                      break;
                  }
	            }
                k=0;              
            }
            else//剩余操作数是奇数
            {
                for(int i=0;i<cn_fu;i++)
                {
                    nums[i]=-nums[i];
                    k--;
                    for(int i=0;i<nums.size();i++)//每次更新数组之后找最小正数
                    {
                        if(nums[i]>0)
                        {
                            minnum=min(minnum,nums[i]);
                        }
                    }
                    if(k==check)
                    {
                        break;
                    }
                }
                    for (int i = 0; i < nums.size(); i++)
                    {
                        if (nums[i] > 0)
                        {
                            minnum = min(minnum, nums[i]);
                        }
                    }
                for(int i=0;i<nums.size();i++)
                {
                    if(nums[i]==minnum)//找到最小正数将其取反
                    {
                        nums[i]=-nums[i];
                        break;
                    }
                }
            }
        }
    }

	int sum = 0;
	for (int i = 0; i < nums.size(); i++)
	{
		sum += nums[i];
	}
	return sum;
}
};

实际上,上面冗长的if分类操作我们只需要再用一个sort就能解决(LeetCode上的题解,感慨自己思维不够敏捷^~^) 

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end());//先排序
        int sum=0;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]<0&&k>0)//一边取反一边求和
            {
                nums[i]=-nums[i];
                k--;
            }
            sum+=nums[i];
        }
        sort(nums.begin(),nums.end());//全部取反之后直接找到最小正数
        if(k%2==0)
        {
            return sum;
        }
        else
        {
            return sum-2*nums[0];//补上减去那个最小正数(本来没减反而加上,所以现在减2倍)
        }
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ZZZWWWFFF_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值