【洛谷 P1667】 数列 (贪心)

题目链接
对于一个区间([x,y]),设这个区间的总和为(S)

那么我们在前缀和(设为(sum[i]))的意义上考虑到原操作其实就是(sum[x−1]+=S) , (sum[x]+S−S) , (sum[y]−=S) , (sum[y+1]+S−S)

而且我们注意到,本来就有(sum[x−1]+S==sum[y]),所以观察到其实原操作只是单纯的交换了一下(sum[x−1])(sum[y])而已,而且这个([x,y])区间任意选择,故原题已经可以改为:

给一个前缀和序列,可以在其中任意交换2个数,最后让这个序列变为单调递增的。

注意:在前缀和序列里不能有负数和相等的,若有就输出-1

--摘自洛谷题解

离散化一下贪心交换就好了。。这个贪心实现有难度

#include <cstdio>
#include <iostream>
#define re register
using namespace std;
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
const int MAXN = 100010;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
	return s * w;
}
int n, ans, num;
int a[MAXN], p[MAXN], s[MAXN];
int cmp(int x, int y){
    return a[x] < a[y];
}
int main(){
	Open("sequence");
	ans = n = read();
	for(re int i = 1; i <= n; ++i){
       a[i] = read() + a[i - 1];
       p[i] = i;
       if(a[i] <= 0){
         printf("-1
");
         return 0;
       }
    }
    for(int i = 1; i <= n; ++i)
       if(a[i] == a[i - 1]){
         printf("-1
");
         return 0;
       }
    sort(p + 1, p + n + 1, cmp);
    for(int i = 1; i <= n; ++i)
       a[p[i]] = i;
    for(int i = 1; i <= n; ++i)
       if(i == a[i])
         --ans;
       else swap(p[a[i]], p[i]), swap(a[p[a[i]]], a[i]);
	printf("%d
", ans);
	return 0;
}

原文地址:https://www.cnblogs.com/Qihoo360/p/9669943.html