题目地址:http://codeforces.com/problemset/problem/301/A
题目的意思,给2*n-1个数,每次在这些数里面取n个数乘以-1,然后问你经过任意次这样的操作后
给出这些数的总和的最大值
也就是说要使得这些数尽量的都为正数,这样和才最大
感谢ACdream群的朋友们,没有你们的点拨,我估计还是想不出来
已知每次要取n个,假设这2*n-1个数里面有k个负数,下面我们可以来讨论一下
1)首先如果k=n的话,就特别好说了,直接*-1就完事,也就是所有数的绝对值之和
2)如果n为奇数
k<n
那么我们可以每次从k个里面取x个负数,从剩下的正数里面取n-x个正数
因为k<n,所以我们可以让负数慢慢增加到n,然后再一次性*-1,解决问题
我们来看n-x和x
减少了x个负数,增加了n-x个负数,也就是总的增加了n-2*x个负数
因为n是奇数,而2*x是偶数,且n不为0
奇数-偶数=奇数,所以在n为奇数的条件下,我们可以增加或减少奇数个负数
这样我们就可以使负数的个数变为n个,然后再*-1解决问题
k>n
在前面提到了,可以减少奇数个,所以我们也可以通过刚刚那种方式解决,这个不用赘述。
那可能为想,增加或减少奇数个k就一定可以为n吗
答案是肯定的,因为1是奇数
3)如果n是偶数
同样还是取x个负数,n-x个正数
一共增加了n-2*x个负数
因为n为偶数,所以每次只能增加或减少偶数个负数
那么这里就要注意了,如果k是奇数的话,每次增加或减少偶数个
怎么也无法变成n
但是如果k是偶数的话,则可以成为n
那如果k是奇数的时候怎么求出最大值呢?
我们将k减少到1,也就说最多只会出现一个负数
因为正负都是可以调换的
所以我们可以先求出所有数的绝对值之和,然后减去最小的那个的二倍
就是我们要求的答案
下面上代码:
#include<cstdio>
using namespace std;
const int maxn = 200+10;
int a[maxn];
int sum;
int main()
{
int n;
int fu;
int min;
while(~scanf("%d",&n))
{
fu=0;
min=0x3f3f3f3f;
sum=0;
for(int i=1;i<=2*n-1;i++)
{
scanf("%d",&a[i]);
if(a[i]<0)
{
a[i]=-a[i];
fu++;
}
if(a[i]<min)
min=a[i];
sum+=a[i];
}
if((n&1) || !(fu&1))
printf("%d\n",sum);
else
printf("%d\n",sum-2*min);
}
return 0;
}
本文解析 CodeForces 301A 的最优解法,通过数学推导说明如何通过有限次数的操作使给定的 2*n-1 个数的总和达到最大值。讨论了不同情况下(如 n 为奇数或偶数,负数个数为奇数或偶数)的操作策略,并提供 C++ 代码实现。
498





