codeforces 1430 F. Realistic Gameplay 思维+dp预处理

很容易发现,如果怪物区间严格不相交,则是一个非常简单的枚举题目。

因为中间空的时间再多,我们也只能上一次子弹,也就是说,每波怪物开始的时候,最多只能存k发子弹。

而题目又保证了只能相交于上一波的终点。

显然我们可以倒着处理,判断每波开始之前,枪里至少要有多少发子弹。

若某一波是独立的则dp[i]=max(0ll,p[i].a-(p[i].r-p[i].l)*k);  除了第一秒后面每秒都能上一次弹。

第一秒需要的子弹数就是dp[i].

而如果当前波结束的时间就是下一波开始的时间,则这两波是关联的,即这一波不仅要打完当前怪兽,还要在最后一天剩下dp[i+1]发子弹(因为中间没有空隙上子弹),则这一波的dp[i]=max(0ll,p[i].a+dp[i+1]-(p[i].r-p[i].l)*k);

若某一波的dp[i]>k则说明无论前面怎么规划,都无法打完所有怪兽。

否则一定能打完,(因为连续的波数我们dp[i]进行了约束,不连续的中间一定有一次上弹的机会,也能满足dp[i])

最后再正向扫一遍累加结果即可!

复杂度On。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define ls (o<<1)
#define rs (o<<1|1)
//#define m (l+r)/2
#define pb push_back
typedef pair<int,int> pii;
const double PI= acos(-1.0);
const int M = 2000+7;
/*
int head[M],cnt=1;
void init(int n){cnt=1;for(int i=0;i<=n;i++)head[i]=0;}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/

struct node{
	ll l,r,a;
}p[M];
ll dp[M];
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	ll n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>p[i].l>>p[i].r>>p[i].a;
	} 
	bool flag=true;
	for(int i=n;i>=1;i--){
		if(p[i].r==p[i+1].l)
			dp[i]=max(0ll,p[i].a+dp[i+1]-(p[i].r-p[i].l)*k);
		else
			dp[i]=max(0ll,p[i].a-(p[i].r-p[i].l)*k);
		if(dp[i]>k)flag=false;
	}
	if(!flag){
		cout<<-1<<endl;//不可能打完所有怪物 
		return 0;
	}
	ll ans=0,tp=k;//总共打多少子弹,当前弹夹有多少子弹 
	for(int i=1;i<=n;i++){
		if(tp<dp[i])ans+=tp,tp=k;//不连续可以上弹,连续的波数倒着dp时已经判断过了 
		ans+=p[i].a;
		if(p[i].a>=tp)p[i].a-=tp,tp=k-p[i].a%k;
		else tp-=p[i].a;
	//	cout<<i<<"  ->   "<<p[i].l<<"  "<<p[i].r<<"  "<<p[i].a<<"   "<<tp<<"    =  "<<ans<<endl;
	}
	cout<<ans<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值