D. Slime(思维,符号放置)

98 篇文章 3 订阅
56 篇文章 1 订阅

D. Slime

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

There are nn slimes in a row. Each slime has an integer value (possibly negative or zero) associated with it.

Any slime can eat its adjacent slime (the closest slime to its left or to its right, assuming that this slime exists).

When a slime with a value xx eats a slime with a value yy, the eaten slime disappears, and the value of the remaining slime changes to x−yx−y.

The slimes will eat each other until there is only one slime left.

Find the maximum possible value of the last slime.

Input

The first line of the input contains an integer nn (1≤n≤5000001≤n≤500000) denoting the number of slimes.

The next line contains nn integers aiai (−109≤ai≤109−109≤ai≤109), where aiai is the value of ii-th slime.

Output

Print an only integer — the maximum possible value of the last slime.

Examples

input

Copy

4
2 1 2 1

output

Copy

4

input

Copy

5
0 -1 -1 -1 -1

output

Copy

4

Note

In the first example, a possible way of getting the last slime with value 44 is:

  • Second slime eats the third slime, the row now contains slimes 2,−1,12,−1,1
  • Second slime eats the third slime, the row now contains slimes 2,−22,−2
  • First slime eats the second slime, the row now contains 44

In the second example, the first slime can keep eating slimes to its right to end up with a value of 44.


假设有四个数a,b,c,d。手动去各种模拟,可以发现:

比如b吃c,再吃d,a再吃b,最后的答案是a-b+c+d

比如a吃b,吃c,然后d吃a,最后是d+c+b-a.

如果发现了这一点,就容易发现其实最后的和是各个数字前面加+/-的,而且+和-至少同时都有一个。

那么看一段有正负的数字,想让最后和最大,就让其中正的+号,负的-号,然后累加。

如果是全正,就让其中最小的成负号。如果全负就让最大的正号。

代码的时候先sum统计一遍绝对值和,然后看序列的情况。注意统计的时候sum是全部正数加的,比如全正该加负号的数也加了正进去,所以减去的时候减两倍。负数同理。


#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=5e5+100;
typedef long long LL;
LL a[maxn];
//保证至少一个+和-号 
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  LL sum=0;LL da=-0x3f3f3f3f,xi=0x3f3f3f3f;
  for(LL i=1;i<=n;i++){
  	cin>>a[i];
	sum+=abs(a[i]);
  	da=max(da,a[i]);
  	xi=min(xi,a[i]);
  }
  if(n==1) cout<<a[1]<<endl;
  else{
  	if(da<0){//全负 
      	cout<<sum+da*2<<endl;	
  	}
  	else if(xi>0){//全正 
  		cout<<sum-xi*2<<endl;
	}
	else{
		cout<<sum<<endl;
	}
  }
return 0;
}


然后看了看cf的官方题解,说是还可以dp,原来我是没想出这个dp怎么写的。

“It could have also been solved with DP where we check if - sign has been taken or not, and + sign has been taken or not. (4nstates)”

就是说dp的定义是定义+和-有无添加过了。

dp[i][1/0][1/0]:共有i个数且到第i个数+/-号有无添加过的最大值

考虑状态转移:

1.如果当前到i这个数且前i个数只添加过+:  dp[i][1][0]=dp[i-1][1][0]+a[i];

2.如果当前到i这个数且前i个数只添加过-:  dp[i][0][1]=dp[i-1][0][1]-a[i];

3.如果到当前i这个数且前i个数加过+和-: 

dp[i][1][1]=max(dp[i][1][1],dp[i-1][1][0]-a[i]);

dp[i][1][1]=max(dp[i][1][1],dp[i-1][0][1]+a[i]);

dp[i][1][1]=max(dp[i-1][1][1],dp[i-1][1][1]+abs(a[i]);//如果添加过了那么最大值看当前这个数是正是负,正直接+,负填负号。


总结:相邻数字的操作可以拿具体例子,也可以设字母相邻去多操作操作。有时候具体容易发现,有时候字母更容易发现,这题就是字母更容易发现。构造出来发现至少有一个+和-,且发现了最后答案就是在原有的式子数字上前面添加+/-的时候,就好思考了。

这题的dp如果没有先思考出构造的方式,是十分难以dp的。思考出来了其实也没必要dp。但是为了锻炼一下dp,还是尝试了dp。

是道不错的题


博客撰写过程中参考了:

https://www.cnblogs.com/dilthey/p/10492657.html

https://blog.csdn.net/jziwjxjd/article/details/106858999

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值