题目
思路
要求
max
∑
b
i
=
i
−
∣
a
i
−
x
∣
\max\sum b_i=_i−∣a_i−x∣
max∑bi=i−∣ai−x∣
可以拆成
∑
b
i
−
min
∣
a
i
−
x
∣
\sum b_i-\min∣a_i−x∣
∑bi−min∣ai−x∣
定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 为第 i i i 时刻在 j j j 位置的最小代价。
发现有些时间是空的,所以稍微改一下: d p [ i ] [ j ] dp[i][j] dp[i][j] 为第 i i i 个烟花绽放时刻在 j j j 位置的最小代价。
易得到: d p [ i ] [ j ] = min d p [ i − 1 ] [ k ] + ∣ a i − x ∣ ( k ∈ [ j − t ∗ ( 0 → d ) , j + t ∗ ( 0 → d ) ] , t = t i − t i − 1 ) dp[i][j]=\min dp[i-1][k]+|a_i-x|\ (k\in [j-t*(0 \to d),j+t*(0 \to d)],t=t_i-t_{i-1}) dp[i][j]=mindp[i−1][k]+∣ai−x∣ (k∈[j−t∗(0→d),j+t∗(0→d)],t=ti−ti−1)
分析一下这个朴素 d p dp dp 的复杂度:
- 空间复杂度: m n mn mn
- 时间复杂度: O ( m n 2 ) O(mn^2) O(mn2)
考虑对两部分进行优化:
- 空间复杂度:由于只会用到前面的一维,所以滚动一下即可。就优化到了 n n n 的空间复杂度
- 时间复杂度:即优化决策时 k k k 的枚举,直接单调队列维护一下最小值即可。
Code:
struct node
{
LL a, b, t;
}Fire[MAXN];
LL Sum, dp[2][MAXN];
deque<LL> Q;
int main()
{
LL n, m, d;
read( n ); read( m ); read( d );
for (Int i = 1; i <= m; ++ i)
{
read( Fire[i].a );
read( Fire[i].b );
Sum += Fire[i].b;
read( Fire[i].t );
}
for (Int i = 1; i <= n; ++ i)
dp[1][i] = Abs(Fire[1].a - i);
for (Int i = 2; i <= m; ++ i)
{
LL Now = i % 2, Pre = ! Now, t = Fire[i].t - Fire[i - 1].t;
memset(dp[Now], 0x3f, sizeof dp[Now]);
while (! Q.empty())
Q.pop_front();
Q.push_back( 1 );
for (Int j = 1; j <= n; ++ j)
{
while (! Q.empty() && Q.front() < j - t * d)
Q.pop_front();
while (! Q.empty() && dp[Pre][Q.back()] > dp[Pre][j])
Q.pop_back();
if (j != 1)
Q.push_back( j );
if (! Q.empty())
dp[Now][j] = Min(dp[Now][j], dp[Pre][Q.front()] + Abs(Fire[i].a - j));
}
while (! Q.empty())
Q.pop_front();
Q.push_front( n );
for (Int j = n; j >= 1; -- j)
{
while (! Q.empty() && Q.front() > j + t * d)
Q.pop_front();
while (! Q.empty() && dp[Pre][Q.back()] > dp[Pre][j])
Q.pop_back();
if (j != n)
Q.push_back( j );
if (! Q.empty())
dp[Now][j] = Min(dp[Now][j], dp[Pre][Q.front()] + Abs(Fire[i].a - j));
}
}
LL Ans = INF;
for (Int i = 1; i <= n; ++ i)
Ans = Min(Ans, dp[m % 2][i]);
printf("%lld", Sum - Ans);
return 0;
}