NOI2007货币兑换CASH 斜率DP CDQ分治

2 篇文章 0 订阅
1 篇文章 0 订阅

按照http://hzwer.com/3508.html改的……


勉强能当自己板子用吧…… 感觉细节好多,


要注意各种细节,比如两个点重合啊,两个点的斜率不存在啊种种种种……


太累了,但是我感觉判断2个点斜率差,可以用我的方法更好一些~


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;


const int maxn = 100005;
const double inf = 1e12;//-inf就是负无穷了
const double eps = 1e-9;
double f[maxn]={0};
struct node
{
	int id;
	double x, y, k;
	double a, b, r;
}p[maxn], t[maxn];


node stk[maxn];
int top, n;

bool operator <(node A, node B)//斜率倒着排序,为了在凸壳上计算方便
{
	return A.k > B.k;
}

bool check(node A, node B, node C)// A,B的斜率如果<=B,C斜率,则为true
{
	double a=(B.y-A.y)*(C.x-B.x);
	double b=(C.y-B.y)*(B.x-A.x);

	if (a==b)	return true;
	if (a>0 && b>0)	return a>b;
	if (a<0 && a<0)	return a<b;
	if (a<0)	return true;
	return false;
}

double xie(node A, node B)
{
	if (A.y==B.y)	return inf;
	return (A.y-B.y)/(A.x-B.x);
}


double getDP(int k, node A)
{
	return p[k].a*A.x +p[k].b*A.y;
}

void solve(int l, int r)
{
	if (l == r)
	{
		f[l]=max(f[l],f[l-1]);
		p[l].y=f[l]/(p[l].a*p[l].r+p[l].b);
		p[l].x=p[l].r*p[l].y;
		return;
	}
	int mid = l + (r-l)/2;
	int l1 =l, l2 = mid + 1;
	for (int i = l; i <=r; ++ i)
		if (p[i].id <= mid)	t[l1++] = p[i];
		else t[l2++] = p[i];
	for (int i = l; i <= r; ++ i)	p[i] = t[i];
	solve(l, mid);
	top = 0;//[a[top]为栈顶元素,top=0表示空栈,栈内有0个元素。]
	//开始做一次凸壳,之前的所有的点,都是按照x坐标排序好了,因为已经不需要求解其f值,已经不需要管顺序了
	
	node tmp;
	for (int i = l; i<= mid; ++ i)
	{
		while (top > 1 && check(stk[top- 1], stk[top], p[i]))	top--;
		stk[++top] = p[i];
	}
	tmp;
	tmp.x=0;
	tmp.y=0;
	stk[++top]=tmp;//为了让最后一个节点出现异常,即位最后一个斜率巨大无比是正数。而递减的序列出现正数,不可能更优

	int pos=1;
	for (int i = mid + 1; i <= r; ++ i)
	{
		while (pos <=top && getDP(i, stk[pos]) <= getDP(i, stk[pos+1]))	pos++;
		f[p[i].id] = max(f[p[i].id], getDP(i, stk[pos]));
	}

	solve(mid + 1, r);
	l1=l, l2=mid+1;
	//现在已经计算完l,r区间的所有值,并且实际上,已经都取得最优解了。需要给他们按照x坐标,归并排序了
	for (int i = l; i <= r; ++ i)
		if (l2 > r|| l1 <=mid && (p[l1].x < p[l2].x || p[l1].x==p[l2].x && p[l1].y>p[l2].y))	t[i] = p[l1++];
		else t[i] = p[l2++];
	for (int i = l; i <=r; ++ i)	p[i] = t[i];
}


int main()
{
	freopen("cash.in","r",stdin);
	freopen("cash.ans","w",stdout);
	scanf("%d%lf",&n, &f[0]);
	for (int i = 1; i <= n; ++ i)
	{
		scanf("%lf%lf%lf", &p[i].a, &p[i].b, &p[i].r);
		p[i].id = i;
		p[i].k = -p[i].a/p[i].b;
		if (p[i].a==0)	p[i].k=0;
	}

	sort(p + 1, p + 1 + n);
	solve(1, n);
	printf("%.3lf\n", f[n]);
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值