codeforces 489e Hiking dp+01分数规划+二分

来源:互联网 发布:羽绒被缺点 知乎 编辑:程序博客网 时间:2024/05/06 23:41

题意:

一个人在起点0,有n个休息点,每个点有两个数值,分别表示距离起点的距离xi,以及所获得的愉悦值bi

这个人打算每天走L距离,但实际情况不允许他这么做。

定义总体失望值val = sum(sqrt(Ri - L)) / sum(bi);   Ri:一天所走的距离。i为所到的节点。

现在要使得val最小(这个人必须要到达最终的节点),让你找出相应的方案,即把所应走的休息点输出。


思路:

一开始用dp,结果是错的,当时也感觉有点不对。

01分数规划,网上找了一下资料以及让铭神解答了一些疑惑,还得多做几题才能熟悉。


对于01分数规划,其实就是求解分数的最值问题。

假如对于原来的式子val = sum(a[i]*x[i])/sum(b[i]*x[i]);//x[i]:1或者0,表示走或不走这个点;

假设answer就是最优值,放到这题来说,也就是说是最小值;为了方便起见,这里的A、B代表最优的方案)

answer = A/B; 

那么对于所有方案有:

answer <= sum(a[i]*x[i])/sum(b[i]*x[i])  ①

移项之后:

sum(a[i]*x[i]) - sum(b[i]*x[i])*answer >= 0 ②

由式子②可看出,最终的answer要使得所有的方案均>=0;


由于现在不知道answer究竟是多少,我们先假设一个值,记为L;

F(L) = sum(a[i]*x[i]) - sum(b[i]*x[i])*L;

可以看出,函数F(L)在方案确定的情况下,L值越大,F(L)值越小,是一个单调递减函数。

但实际上,即使方案不固定,F(L)的值也是随着L的增大而减少。

(图中斜线代表方案,横坐标代表变量L)当L增大的时候,所有的方案F(L)值都会减少。


既然这样,我们就可以二分L来找answer。


二分L,找到使sum(a[i]*x[i])-sum(b[i]*x[i])*L 的最小的方案(对于本题,这个就是用dp实现)

*以下用A[i]表示sum(a[i]*x[i]),B[i]表示sum(b[i]*x[i]);

如果F(L) > 0,则有L < A[i]/B[i](虽说此时的L已经使所有方案均>=0,但此时的L是取不到的。因为A[i]/B[i]已经是当前最小的了,而L却更小)此时应该把下界上移,即l = mid;

如果F(L)< 0,则有L > A[i]/B[i],说明L太大,其实可以再小一点。因为A[i]/B[i]就是更优的方案。此时应该把上界下移,即r = mid;


类似的,如果是求最大值,则只要把式子①改为”>=“。


*由于也才刚学的01分数规划,本想说得更通用点,限于目前的理解,只能到这里。如果上文有哪里不对的话,还望指正。

code:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int MAXN = 1e3+5;const int INF = 0x3f3f3f3f;int n, l;int x[MAXN], b[MAXN];int par[MAXN];double dp[MAXN];void getRoute(int i){    if(i == 0) return;    getRoute(par[i]);    cout<<i<<" ";}bool check(double L){    dp[0] = 0;    for(int i = 1;i <= n; i++)    {        dp[i] = INF;        for(int j = 0;j < i ;j++)        {            double tmp = dp[j] + sqrt(1.0 * abs(x[i]-x[j]-l)) - L*b[i];            if(tmp < dp[i])            {                dp[i] = tmp;                par[i] = j;            }        }    }    if(dp[n] < 0) return true;    return false;}    void solve(){    double l = 0, r = 1e10;    while(r-l > 1e-8)    {        double mid = (l+r)/2;        if(check(mid))            r = mid;        else             l = mid;    }    getRoute(n);    cout<<endl;}            int main(){    ios::sync_with_stdio(false);    cin>>n>>l;    for(int i = 1;i <= n ;i++)        cin>>x[i]>>b[i];    solve();    return 0;}


0 0
原创粉丝点击