欧元兑换(斜率优化DP+贪心)

【问题描述】

你每天会收到到一些欧元,可能正也可能负,银行允许你在某天将手头所有的欧元

兑换成人民币。第 i 天的兑换比率是 1 欧元 : i 元人民币,同时你必须再多付出 T 元人

民币被银行收取。在 N 天你必须兑换所有持有的欧元。

你的任务是找一个方案使第 N 天结束时能得到最多的人民币。

【输入格式】

第一行是整数 N, T

第二行是 N 个整数 Ci,表示第 i 天开始时得到 Ci 的欧元。

【输出格式】

输出最终得到的最多人民币。

【样例】

euro.in euro.out

7 1

-10 3 -2 4 -6 2 3

17

【样例说明】样例可在第157天的时候兑换。

【数据规模】

100%的数据:1 ≤ N ≤ 345670 ≤ T ≤ 34567,每天收到的欧元在[-10001000],结

果保证在long long范围内。

 

题解

首先考虑最朴素的DP:设 f[ i ] 表示前i天兑换欧元所能得到的最大收益

f[ i ]=max{f[j]+(s[i]-s[j])*i-T};

恩,看到长成这样的东西,我们很容易想到斜率优化dp

整理一下,f[i]=max{f[j]+s[i]*i-s[j]*i-T}

假设 j 就是我们要找的最优解,f[i]=f[j]+s[i]*i-s[j]*i-T;

f[j]=s[j]*i+f[i]-s[i]*i+T;

把 i 看做斜率 k ,s[j]看成x。。等等,s[j]不是单调的,麻烦了。

怎么办呢,考虑整个序列,根据题(chang)意(shi)可得,每一个兑换的时间点(最后一次除外)都是收到负数的欧元时进行的

可以感性的理解一下,就是不会再还能挣钱的时候就抛了,等亏钱再抛

比如说现在是8 -9(第3天,第4天)如果在第3天换的话是3*8-4*9=-12 第4天换就是(8-9)*4=-4

这样我们就可以考虑在每一个可能抛出的点进行DP

弄一个前缀和s记录从上次值为负到这次值为负之间的数之和

然后对这个东西斜率优化,显然b[i].s是单调递减的,因为每一个元素都是负的

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=40005;
int a[N];
long long s[N],q[N],f[N];
struct node
{
	long long x;
	int id;
}b[N];
double xielv (int x,int y)
{
	return 1.0*(f[x]-f[y])/(s[x]-s[y]);
}
int main()
{
	freopen("euro.in","r",stdin);
	freopen("euro.out","w",stdout);
	int i,n,m=0,t,l=1,r=1;
	scanf("%d%d",&n,&t);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]+=a[i-1];
		if(a[i]<0)
		{
			b[++m].x=a[i];
			b[m].id=i;
			a[i]=0;
		}
	}
	b[++m].x=a[n];
	b[m].id=n;
	for(i=1;i<=m;i++)
		s[i]=s[i-1]+b[i].x; 
	for(i=1;i<=m;i++)
	{
		while(l<r&&xielv(q[l],q[l+1])<1.0*b[i].id)
			l++;
		f[i]=f[q[l]]+(s[i]-s[q[l]])*b[i].id-t;
		while (l<r&&xielv(q[r-1],q[r])>xielv(q[r],i))
			r--;
		q[++r]=i;
	}
	printf("%lld\n",f[m]);
	return 0;
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值