HT-018 Div3 能量消耗 题解 [ 绿 ] [ 线性 dp ] [ 前缀和优化 ]

能量消耗:一个前缀和优化 dp 的大典题,要是数据水一点 O(n3) 都能硬草过去。

思路

显然,定义 dp[i] 为考虑前 i 个塔,并且将第 i 个塔开启,将前面的精灵全部收集的最小代价。

于是转移:

dp[i]=min(dp[i],dp[j]+w(j,i)+c[i])

其中 0j<imw(j,i) 表示收集从塔 ji 的所有精灵到塔 i 所花费的代价。

总体复杂度 O(n3)1000 数据卡的有点紧,我们尝试优化。

前缀和优化

对于 w(j,i) ,发现它可以由 w(j,i1) 和他们之间的精灵计算得来。

于是我们枚举每一个起点 j ,后面扫一遍所有塔确定 i ,顺带用一个指针维护。

这题恶心之处就在于这个指针的边界特判。

O(n2) 计算完 w(j,i) 后, dp 复杂度就降为 O(n2) 了,可以过。

坑点

因为 dp[i] 表示的是第 i 个塔开启时的最小花费,所以可能出现最后一个塔不开启的情况,答案不一定是 dp[m]

因此,我们统计答案的时候,只需要找满足位置大于等于最后一个精灵的位置的塔的 dp 值,取一个 min 即可。

场上没判这点,怒挂 20pts 。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
ll a[1005],b[1005],c[1005],n,m,w[1005][1005],lst[1005],dp[1005],ans=0x3f3f3f3f3f3f3f3f;
int main()
{
	freopen("energy.in","r",stdin);
	freopen("energy.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	cin>>m;
	for(int i=1;i<=m;i++)cin>>b[i];
	for(int i=1;i<=m;i++)cin>>c[i];
	lst[0]=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=n;j>=1;j--)
		{
			if(a[j]<=b[i])
			{
				lst[i]=j;
				break;
			}
		}
	}
	b[0]=-0x3f3f3f3f;
	for(int i=0;i<m;i++)
	{
		ll now=0,sm=0,p=lst[i]+1,lpos=0;
		for(int j=i+1;j<=m;j++)
		{
			while(p<=lst[j])
			{
				sm+=now*(a[p]-lpos);
				now++;
				lpos=a[p];
				p++;
			}
			sm+=now*(b[j]-lpos);
			lpos=b[j];
			w[i][j]=sm;
		}
	}
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<i;j++)
		{
			dp[i]=min(dp[i],dp[j]+w[j][i]+c[i]);
		}
	}
	for(int i=1;i<=m;i++)
	{
		if(a[n]<=b[i])ans=min(ans,dp[i]);
	}
	cout<<ans;
	return 0;
}
posted @   KS_Fszha  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示