[CF 372C] Watching Fireworks is Fun

题目

洛谷

思路

要求 max ⁡ ∑ b i = i − ∣ a i − x ∣ \max\sum b_i=_i−∣a_i−x∣ maxbi=iaix
可以拆成 ∑ b i − min ⁡ ∣ a i − x ∣ \sum b_i-\min∣a_i−x∣ biminaix

定义 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[i1][k]+aix (k[jt(0d),j+t(0d)],t=titi1)

分析一下这个朴素 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值