问题分析
题意就是每次电梯停止,都会引起想要在更高层停的同学的怒火,还有本想要在更低层停却并没有停的同学的怒火,求最后最少会引起多少的怒火。
蒻当时想了会,感觉是dp,但是这道题有不同做法,dp的
O(N2)
O
(
N
2
)
做法只是其中一种。虽然有
O(N)
O
(
N
)
的做法,但是蒻不太会,所以只提及dp的做法。
做dp想出状态方程就好做了。
到当前第
i
i
层电梯停止至此总共会引起的最少怒火是多少就是本题的状态,我们设为
anger[i]
a
n
g
e
r
[
i
]
,至于边界就是当电梯还未开始,设在第
0
0
层,引起的怒火为。
那该怎么求呢?我们前面提及了,如果在每一层停止引起的怒火由哪几部分组成。我们这里假设电梯停止时,想要在更高层停的同学的怒火为
sum
s
u
m
,想要在更底层停的同学的怒火为
skipAnger
s
k
i
p
A
n
g
e
r
。
所以有
anger[i]=min(anger[i],anger[j]+skipAnger+sum)(1≤i≤n,0≤j≤i−1)
a
n
g
e
r
[
i
]
=
m
i
n
(
a
n
g
e
r
[
i
]
,
a
n
g
e
r
[
j
]
+
s
k
i
p
A
n
g
e
r
+
s
u
m
)
(
1
≤
i
≤
n
,
0
≤
j
≤
i
−
1
)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1500+3, inf = 1e9+7;
int anger[N],S[N];
int t,n;
int main()
{
//freopen("in.txt","r",stdin);
cin>>t;
while(t--)
{
cin>>n;
int sum = 0;
for(int i = 1; i <= n; ++i){
cin>>S[i];
sum += S[i];
}
anger[0] = 0;//初始边界
for(int i = 1; i <= n; ++i){
sum -= S[i];//想要去更高层的同学
int skipAnger = 0;
anger[i] = inf;
//从前面每次停止递推
for(int j = i-1; j >= 0; --j){
anger[i] = min(anger[i],anger[j]+sum+skipAnger);
skipAnger += (i-j)*S[j];//需要走楼梯返回他们想要去的楼层的同学的怒火
}
}
cout<<anger[n]<<endl;
}
return 0;
}