B. Array Cancellation(思维+贪心)Codeforces Round #668 (Div. 2)

原题链接: https://codeforces.com/contest/1405/problems

B. Array Cancellation(思维+贪心)Codeforces Round #668 (Div. 2)_第1张图片
测试样例

input
7
4
-3 5 -3 1
2
1 -1
4
-3 2 -3 4
4
-1 1 1 -1
7
-5 7 -6 -4 17 -13 4
6
-1000000000 -1000000000 -1000000000 1000000000 1000000000 1000000000
1
0
output
3
0
4
1
8
3000000000
0

Note

Possible strategy for the first test case:

  • Do (i=2,j=3) three times (free), a=[−3,2,0,1].
  • Do (i=2,j=1) two times (pay two coins), a=[−1,0,0,1].
  • Do (i=4,j=1) one time (pay one coin), a=[0,0,0,0].

题意: 给你一个数组 a a a,你两种操作可以进行:当你选择下标为i i i i j j j时,且 i < j ii<j时,我们可以免费让 a i − − , a j + + a_i--,a_j++ ai,aj++,而当 i > j i>j i>j时我们需要消耗 1 1 1硬币使得 a i − − , a j + + a_i--,a_j++ ai,aj++,我们的目的是要使得所有的元素都为0,由于其中保证数组之和为0,说明一定有解,请你输出消耗最少的硬币数。

解题思路: 往死里贪,既然可以免费,那就把免费的机会都用掉,那么我们自然是要遍历一遍数组找大于0的数,再从这数之后一直遍历找小于0的数相互抵消。 遍历完之后,我们接下来进行的所有操作都是付费的,则我们接下来无需考虑谁和谁组合**,因为负数之和必定等于正数之和的相反数,而我们的操作就是让负数+1,正数-1,说明我们只要统计正数之和即可,这就是我们啊哟消耗的硬币数。**
纠正:以上的操作是暴力做法,时间复杂度是非常大的,很容易超时,并不适合解这个题目。 接下来提供一种非常优良的算法,比第一种暴力好太多,但思想是一样的思想,我们关键是要进行动态处理。我们知道,如果负数前面的正数都被用完了,那么说明这个负数减为0是需要它之后的正数的,这操作自然是付费的。这就是我们进行动态处理的核心,我们用sum实时统计前i个数的和。 如果是小于0的说明前i个数中有负数没有变为0,那么我们自然是要花硬币去使得它们变为0,那么我们这个时候就要保存我们的所花硬币数,那么之后同样是利用sum去存储。(注意,这里sum应该要看成两部分:剩余的负数(要硬币消耗的数量)+没有用掉的正数,这个非常关键,也是理解这个算法的核心。) 所以我们要用ans存储最小值也就是这个道理,就是存储剩余的负数而不涵盖没有用掉的正数。OK,具体看代码。(两种方法都贴了,前一种只是为了做对比,推荐理解后一种)

超时AC代码(不推荐,跑得很慢)

/*
*邮箱:[email protected]
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include	//POJ不支持
 
#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair
 
using namespace std;
 
const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//
 
int t,n,a[maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
		while(t--){
			cin>>n;
			rep(i,0,n-1){
				cin>>a[i];
			}
			rep(i,0,n-1){
				if(a[i]>0){
					for(int j=i+1;j<n;j++){
						if(a[j]<0){
							if(a[j]*(-1)>a[i]){
								a[j]+=a[i];
								a[i]=0;
								break;
							}
							else{
								a[i]+=a[j];
								a[j]=0;
							}
						}
					}
				}
			}
			ll ans=0;
			rep(i,0,n-1){
				if(a[i]>0){
					ans+=a[i];
				}
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

动态处理AC代码(推荐,大家都说好)

/*
*邮箱:[email protected]
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5+4;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int t,n,a[maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
		while(t--){
			cin>>n;
			rep(i,1,n)
				cin>>a[i];
			ll sum=0,ans=0;
			//这里要注意一个性质,就是数组元素之和为0,且我们进行的操作也不会改变这个性质。
			rep(i,1,n){
				sum+=a[i];//实时统计,因为我们的免费操作是将ai--,aj++(i
				ans=min(ans,sum);//保存结果,因为sum是会变的,这里不要误解为存储最小值。
			}
			cout<<(-1)*ans<<endl;
		}
	}
	return 0;
}

你可能感兴趣的:(贪心算法,#,CF,贪心算法)