挖宝

挖宝(二分答案+dp)

n个点,有m条边连接。小R从第一个点开始走,且必须从编号小的点走到编号大的点,直到走到第n个点。 每条边有时间\(t[i]\),价值\(w[i]\),求\(max(\frac{\sum w[i]}{\sum t[i]})\)

先考虑直接dp出答案,怎么设计状态呢?我们发现分数是不能累加的,导致转移不了状态。所以可不可以通过枚举答案,然后验证解来解题呢?如果当前枚举的答案是A,说明\(\sum w[i]-\sum t[i]*A=0\)。于是这样,我们就把分数的状态转化成了整数,可以进行累加了。\(f[i]\)表示前i个点,\(\sum w[i]-\sum t[i]*A\)的最大值。k是i的后继点,那么\(f[k]=max(f[i]+w[k, i]-t[k, i]*A, f[k])\)。如果\(f[k]>0\),说明答案比A大,否则说明答案比A小。于是二分答案即可。

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=1e4+5, maxm=1e5+5, INF=1e9;
const double eps=1e-5;

class Graph{
public:
    struct Edge{
        int to, next, t, w; Graph *bel;
        Edge& operator ++(){ return *this=bel->edge[next]; }
        inline int operator *(){ return to; }
    };
    void addedge(int x, int y, int t, int w){
        Edge &e=edge[++cntedge]; e.bel=this;
        e.to=y; e.next=fir[x]; e.t=t; e.w=w;
        fir[x]=cntedge;
    }
    Edge& getlink(int x){ return edge[fir[x]]; }
private:
    int cntedge, fir[maxn];
    Edge edge[maxm*2];
};

Graph g;
int n, m;
double f[maxn];

bool ok(double A){
    fill(f, f+maxn, -INF);
    f[1]=0; Graph::Edge e;
    for (int i=1; i<=n; ++i){
        e=g.getlink(i);
        for (; *e; ++e){
            if (*e<i) continue;
            f[*e]=max(f[*e], f[i]+e.w-e.t*A);
        }
    }
    return f[n]>0;
}

int main(){
    scanf("%d%d", &n, &m);
    int a, b, c, d; double l=0, r=1e5, mid;
    for (int i=0; i<m; ++i){
        scanf("%d%d%d%d", &a, &b, &c, &d);
        if (a>b) swap(a, b);
        g.addedge(a, b, c, d);
    }
    while (r-l>eps){
        mid=(r+l)/2;
        if (ok(mid)) l=mid; else r=mid;
    }
    printf("%.4lf", mid);
    return 0;
}

转载于:https://www.cnblogs.com/MyNameIsPc/p/7770531.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值