topc PB
MCU_NL
problem
- 給予一組數組,求最大總和
- 給予n -> n個數字 0 < n <= 3e5
- 每個數字都在 -1e9 ~ 1e9 內
- 兩種操作
- 將單一區間value * 2,且必須先操作 or 不操作
- 將多個單點 value * -1 (不能操作在已經*2的區間內)

solve
- 由於區間乘二必須優先做 假定當前區間為全部範圍
原陣列:[-2, 5, -3, 4, -1]
- 開始往內縮
[-2, 5, -3, 4] -1
這時會賺 3倍的 -1, 原陣列需*2 在考慮接下來*-1 故為三倍
[-2, 5, -3] 4 -1
這時會虧一個 4 因爲4原本可以*2 但我們一定不會將其*-1
由此方法可以把每個單點的虧跟賺轉成單一的數值
總結:對於負數n 我們的單點數值是 abs(n*3)
對於正數我們的單點數值為 -n
example
a = [-2, 5, -3, 4, -1]
b = [6]
a = [-2, 5, -3, 4, -1]
b = [6, -5]
...
b = [6 -5 9 -4 3]
這時可以抓到 -5 為區間最小
真實的答案為 -5這個位置的原陣列位置 5*2
其餘負數*-1 之總和 20
sum([2, 10, 3, 4, 1])
for(int k=0;k<n;k++){
if(a[k]>=0)b[k]=-a[k];
else b[k]=abs(a[k]*3);
}取區間最小
- 做成前綴和後sliding window
- 前綴和:p = [0, 6, 1, 10, 6, 9]
- setup l, r 區間,並在總和>0時更新左邊界
int l=0, rl=0, rr=-1, mm=INT_MAX;
for(int k=1; k<=n; k++){
if(p[k]-p[l] > 0){
l = k; // 更新左邊界
}
else {
if(p[k]-p[l] < mm){
mm = p[k] - p[l]; // 更新最小區間總和
rr = k-1; // 記錄右邊界 -> 由於是區間和,需要微調位置(對應到原本陣列)
rl = l; // 記錄左邊界
}
}
}
- 這時將抓到的區間*2 剩餘的都取abs即為ans
AC code
int32_t main(){
IO;
int n;cin>>n;
int a[n],b[n],ans=0;for(auto&k:a)cin>>k;
for(int k=0;k<n;k++){
if(a[k]>=0)b[k]=-a[k];
else b[k]=abs(a[k]*3);
}
vector<int>p(1);
for(auto&k:b)p.push_back(p.back()+k);
int l=0,rl=0,rr=-1,r=0,mm=INT_MAX;
for(int k=1;k<=n;k++){
if(p[k]-p[l]>0){
l=k;
}
else {
if(p[k]-p[l]<mm){
mm=p[k]-p[l];
rr=k-1;
rl=l;
}
}
}
for(int k=0;k<n;k++){
if(k>=rl&&k<=rr)ans+=a[k]*2;
else ans+=abs(a[k]);
}
cout<<ans;
}topc PB
By rogerdeng
topc PB
- 96