牛客网周周练15.B背包维护问题
题目描述:
你有一个长度为 n 序列 {a}(序列下标从1开始) ,每次可以从任意位置 i 花费
ai*i 的代价来把 ai 删除。
注意,删除后 ai 后面的数会依次向前补上(下标 -1 ) 。
求把整个序列删完的最小代价。
输入描述
第一行一个整数 n ,第二行 n 个整数代表该序列。
输出描述
一行一个整数表示删完序列的最小代价。
示例
输入:
3 2
输出:
5
备注
1<=n<=10^6,|ai|<=10^7
保证答案在 (-2^63,2^63-1) 范围内
思路分析:
假设全是正数,很容易思考,每次都抽去第一个数字,最终带来的代价最低
就是
c
o
s
t
=
∑
i
=
1
n
a
i
cost=\sum\nolimits_{i=1}^{n}ai
cost=∑i=1nai,因为每次抽取第一个,每次抽出来的序号都是1,即所有的ai * i都是ai*1.此时最小;
事实上,有正数,有负数,我们很容易想到,尽可能让负数的出来的代价尽可能大,即ai * i,我们假设后者叫做阶,即消除的时候的阶尽可能大,最大自然就是原始位置的序号i,最小就是1;
从后往前,依次输出负数,因为是倒序输出负数,所以每次剩下的负数并不会阶减小;
例如:
4 -5 5 -8 -7 6
1: 4 -5 5 - 8 6 cost=-7*5+0=-35;
我们发现剩下的-8,其在-7除去后,他在队伍中的位置序号并没有减小
所以通过倒序输出负数,能使得所有负数都以最低的代价输出(越小越好)
输出完负数后,接下来就将剩余的正数从头开始输出即可;
正确性证明:
对于负数,我们都让他以
a
i
∗
i
ai*i
ai∗i的代价输出了,而对于正数,我们都让他们
以
a
i
∗
1
ai*1
ai∗1的代价输出了;所以,最终这个代价一定是最小的;
坑点:
1:给的示例都是整数,但是看
∣
a
i
∣
|ai|
∣ai∣就知道,应该是有负数输入的
2:保证结果在
(
−
2
63
,
2
63
)
(-2^{63},2^{63})
(−263,263),int在一般是4字节,32位,结果是在
(
−
2
31
,
2
31
−
1
)
(-2^{31},2^{31}-1)
(−231,231−1)内
于是考虑long long 八个字节,
(
−
2
63
,
2
63
−
1
)
(-2^{63},2^{63}-1)
(−263,263−1)
代码:
#include<iostream>
using namespace std;
int main() {
int n; //表示输入多少个数
cin >> n;
long long data; //接受每次输入的数据,相当于a[i];
long long ans = 0;
for (int i = 0; i < n; i++) {
cin >> data;
ans += data >= 0 ? data : data * (i + 1); //对于负数,都已ai*i,对于非负数,都以ai*1
}
cout << ans;
return 0;
}