51Nod-1053-最大M子段和 V2
阿新 • • 發佈:2019-01-07
描述
題解
這個題需要用到貪心搞。
首先涉及到一點優化是,連續的正數或者連續的負數,到最後肯定是可以合併在一起的,所以我們首先將序列中的相鄰正數或者相鄰負數全部合併,將序列壓縮,也許這個也談不上什麼優化,因為這個步驟對於整個貪心過程是必須的,在這個過程中,累計下來所有正數的和。
此時,我們可以發現,新的序列是一個一正一負交替的序列,貌似叫做擺動序列吧,那麼此時我們可以考慮合併(如果需要的話),合併時我們需要貪心,每次先找最小的(絕對值),如果最小的是負數,我們就加入它,相當於將它前後的數連結在了一起,成為了一個區間,反之,我們就需要刪除它,相當於捨去了一個正數區間。這裡不難理解,每次我們都需要對最小代價進行修改,因為如果只考慮正數或者負數,那麼顯然會因為添加了絕對值過大的負數而使結果不能最優,也會存在因為刪除了過大的正數而比新增一個小的負數產生的損失更大。
至於這裡的合併過程,我們可以用連結串列優化,自己實現一個模擬的連結串列,挺簡單的,合併的過程中,將左右的值新增到中間,然後刪除左右兩邊即可,另外用
程式碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
const int MAXN = 2e6 + 10;
template <class T>
inline bool scan_d(T &ret)
{
char c;
int sgn;
if (c = getchar(), c == EOF)
{
return 0; // EOF
}
while (c != '-' && (c < '0' || c > '9'))
{
c = getchar();
}
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0' && c <= '9')
{
ret = ret * 10 + (c - '0');
}
ret *= sgn;
return 1;
}
ll A[MAXN];
int n, m, now;
int pre[MAXN];
int net[MAXN];
set<pair<ll, int> > a;
void _erase(int x)
{
int l = pre[x], r = net[x];
if (l)
{
net[l] = r;
}
if (r)
{
pre[r] = l;
}
}
int main()
{
scan_d(n), scan_d(m);
int cnt = 0;
ll tmp = 0, ans = 0;
// 將相鄰正數和負數分別合併
for (int i = 1, x; i <= n; ++i)
{
scan_d(x);
if ((tmp < 0 && x > 0) || (tmp > 0 && x < 0))
{
now += tmp > 0;
A[++cnt] = tmp;
a.insert(make_pair(abs(tmp), cnt));
tmp = 0;
}
tmp += x;
ans += x > 0 ? x : 0;
}
now += tmp > 0;
A[++cnt] = tmp;
a.insert(make_pair(abs(tmp), cnt));
// 構造連結串列結構
for (int i = 1; i <= cnt; ++i)
{
pre[i] = i - 1;
net[i] = i + 1;
}
net[cnt] = A[0] = 0;
while (now > m)
{
int x = (*a.begin()).second;
a.erase(a.begin());
if ((A[x] < 0 && (!pre[x] || !net[x])) || !A[x])
{
continue;
}
a.erase(make_pair(abs(A[pre[x]]), pre[x]));
a.erase(make_pair(abs(A[net[x]]), net[x]));
ans -= abs(A[x]);
A[x] = A[x] + A[pre[x]] + A[net[x]];
_erase(pre[x]);
_erase(net[x]);
a.insert(make_pair(abs(A[x]), x));
now--;
}
printf("%lld\n", ans);
return 0;
}