Watching Fireworks is Fun(动态规划,单调队列)

文章描述了一个关于烟花放置的问题,目标是最大化在烟花燃放过程中所能获得的幸福值。通过动态规划和单调队列优化的方法,计算出在限制移动速度的情况下,在每个烟花燃放后站在街上不同位置的最大幸福值。最后给出了C++代码实现这一算法。
摘要由CSDN通过智能技术生成

题目

 有一条街,这条街可以被分成n节,记为1,2,3……,n。同时有若干烟花要在街上放,每个烟花都有自己放的时间和幸福值和最佳观看地点(街上的哪一节),烟花时你获得的幸福值就是该烟花的幸福值减去你所站的位置离最佳观看位置的距离,你每秒最多能移动d个单位,求所有烟花放完后你的幸福值最大可以是多少。

思路

我们可以知道,直接解决整个问题是很难的,但是如果我们考虑中间放的第i个烟花,此时要取得最大的幸福值,只需要枚举前i-1个烟花放完后站在街上的每一节的幸福值最大值进行计算,再找出这些计算结果的最大值就可以了,于是就化为了最优子结构,想到运用dp来解题,dp[i][j]表示放完第i个烟花时在第j节的最大幸福值,状态转移方程为dp[i][j]=max(dp[i][j],dp[i-1][k]+b[i]-abs(a[i]-j))【j-d*(t[i]-t[i-1])<=k<=j+d*(t[i]-t[i-1])】,当然j-d*(t[i]-t[i-1])和j-d*(t[i]-t[i-1])不能超出界限,但是直接这样做会tle,我们还需要优化这种做法。我们注意到对于每个dp[i][j],b[i]-abs(a[i]-j)是不变的,要找出其最大值,就是让dp[i-1][k]最大,然后k明显是有一个范围,因此不难看出可以使用单调队列进行优化,具体实现上,因为可以从j往右走,也可以从j往左走,所以我们进行两次滑动窗口,一次往右,一次往左,这道题就解决了。

代码

#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
using namespace std;
#define int long long
const int inf=1e18;
typedef struct pa{
	int idx,num;
}pa;
int a[305],b[305],t[305];
int dp[2][150005];
deque<pa>q;
int max(int a,int b){
	return a>b?a:b;
}
signed main(){
	int n,m,d;cin>>n>>m>>d;
	for(int i=1;i<=m;i++){
		cin>>a[i]>>b[i]>>t[i];
	}
	int flag=1;
	for(int i=1;i<=m;i++,flag=!flag){
		for(int j=1;j<=n;j++){
			while(!q.empty()&&q.front().idx<j-d*(t[i]-t[i-1]))q.pop_front();
			while(!q.empty()&&q.back().num<=dp[!flag][j])q.pop_back();
			pa temp;temp.idx=j;temp.num=dp[!flag][j];q.push_back(temp);
			dp[flag][j]=q.front().num+b[i]-fabs(j-a[i]);
		}
		q.clear();
		for(int j=n;j>=1;j--){
			while(!q.empty()&&q.front().idx>j+d*(t[i]-t[i-1]))q.pop_front();
			while(!q.empty()&&q.back().num<=dp[!flag][j])q.pop_back();
			pa temp;temp.idx=j;temp.num=dp[!flag][j];q.push_back(temp);
			dp[flag][j]=max(q.front().num+b[i]-fabs(j-a[i]),dp[flag][j]);
		}
		q.clear();
	}
	int ans = -inf;
	for(int i=1;i<=n;i++)ans=max(ans,dp[!flag][i]);
	cout<<ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值