CF489E Hiking 01分数规划

一.题目

在坐标轴上有 n ( 1 ≤ n ≤ 1000 ) n(1≤n≤1000) n(1n1000)个景点,第i个景点坐标为 x i ( 1 ≤ x i ≤ 1 0 6 ) x_i(1≤x_i≤10^6) xi(1xi106),美丽度为 b i ( 1 ≤ b i ≤ 1 0 6 ) b_i(1≤b_i≤10^6) bi(1bi106)。数据保证xi严格递增。一个人从坐标0出发,每天行进一段大于0的距离到达坐标更大的景点,他希望每天正好行进 L ( 1 ≤ L ≤ 1 0 5 ) L(1≤L≤10^5) L(1L105)距离,但他只能在景点停下。如果他某天的行进距离为r,那么当天的不满足度为 ∣ L − r ∣ \sqrt{|L-r|} Lr ,同时他会获得当天旅程结束所停在的景点的美丽度。最终他要停在n号节点。求使得 ∑ ∣ L − r ∣ ∑ b \frac{\sum\sqrt{|L-r|}}{\sum b} bLr 取最小值的任意方案,其中 ∣ L − r ∣ {\sqrt{|L-r|}} Lr 表示每天的不满足度之和, ∑ b \sum b b表示每天停留的景点的美丽度之和。
传送门

二.Solution

这道题目是01分数规划的板题。
首先考虑二分,我们二分一个 m i d mid mid,使
∑ ∣ L − r ∣ ∑ b ≤ m i d \frac{\sum\sqrt{|L-r|}}{\sum b}\leq mid bLr mid
→ ∑ ∣ L − r ∣ − m i d ∗ ∑ b ≤ 0 \rightarrow \sum\sqrt{|L-r|}-mid*\sum b\leq 0 Lr midb0
→ ∑ ∣ L − ( x [ i ] − x [ j ] ) ∣ − m i d ∗ ∑ b ≤ 0 \rightarrow \sum\sqrt{|L-(x[i]-x[j])|}-mid*\sum b\leq 0 L(x[i]x[j]) midb0
于是我们写出一个方程式:
f [ i ] = f [ j ] + ∣ L − ( x [ i ] − x [ j ] ) ∣ − m i d ∗ b [ i ] f[i]=f[j]+\sqrt{|L-(x[i]-x[j])|}-mid*b[i] f[i]=f[j]+L(x[i]x[j]) midb[i]
f [ n ] ≤ 0 f[n]\leq 0 f[n]0 m i d mid mid符合条件。
对于输出方案,我们用存前驱的方法,递归输出答案就行了。
初始值 f [ 0 ] = 0 f[0]=0 f[0]=0,其他为最小值。

三.Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;

#define M 1005

int n, pre[M], ans[M];
double L, x[M], b[M], f[M];

void print (int a){
	if (! a)
		return ;
	print (pre[a]);
	printf ("%d ", a);
}
bool check (double lim){
	memset (pre, 0, sizeof pre);
	f[0] = 0;
	for (int i = 1; i <= n; i ++){
		f[i] = -0x7f7f7f7f;
		for (int j = 0; j < i; j ++){
			if (f[j] + lim * b[i] - sqrt (fabs (L - (x[i] - x[j]))) > f[i]){
				f[i] = f[j] + lim * b[i] - sqrt (fabs (L - (x[i] - x[j])));
				pre[i] = j;
			}
		}
	}
	if (f[n] >= 0){
		for (int i = 0; i <= n; i ++)
			ans[i] = pre[i];
		return 1;
	}
	return 0;
}
int main (){
	scanf ("%d %lf", &n, &L);
	for (int i = 1; i <= n; i ++)
		scanf ("%lf %lf", &x[i], &b[i]);
	double l = 0, r = 1e9, mid;
	while (r - l > 1e-9){
		mid = (l + r) / 2.0;
		if (! check (mid))
			l = mid + 1e-9;
		else
			r = mid - 1e-9;
	}
	print (n);
	return 0;
}

Thanks!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值