洛谷p3778[APIO2017]商旅

【洛谷p3778】【APIO2017】商旅

题面

洛谷

题解

01分数规划水题...
设总收益为\(x\)总代价为\(y\),那么题目中求的就是\(\frac{x}{y}<=z\)的最小取值。
把这个式子变个形:\(x-yz<=0\),然后二分\(z\)
我们可以\(O(n^3)\)预处理出最大收益\(pro_{i,j}\)和这张图每2个点之间的最短耗时(其实就是最短路)\(d_{i,j}\)
然后连边\(d_{i,j}*z-pro_{i,j}\)\(spfa\)判一下负环即可。

代码

#include <bits/stdc++.h>

typedef long double db;  
const db eps = 1e-9;
const int maxn = 110;
const int maxk = 1010;
const int inf = 0x3f3f3f3f;   

template<class t> inline void read(t& res) {
    res = 0;  char ch = getchar();  bool neg = 0;
    while(!isdigit(ch))
        neg |= ch == '-', ch = getchar();
    while(isdigit(ch))
        res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
    if(neg)
        res = -res;  
}
inline void cmin(int& a,int b) {
    if(a > b)
        a = b;
}
inline void cmax(int& a,int b) {
    if(a < b)
        a = b;    
}

int S[maxk][maxk], B[maxk][maxk];  
int pro[maxk][maxk], d[maxn][maxn], cnt[maxn];  
int hd[maxn], ver[maxn * maxn], nxt[maxn * maxn];  db wei[maxn * maxn];    
db dis[maxn];  bool vis[maxn];  
int n, m, i, j, k, K, cnte;

inline bool equal(db a,db b) { return b - a < eps; }
inline void adde(int u,int v,db w) {
    ver[++cnte] = v;  wei[cnte] = w;   
    nxt[cnte] = hd[u];  hd[u] = cnte;  
}

inline bool check(db mid) {
    std::queue<int> q;
    memset(hd,-1,sizeof(hd));  cnte = 0;
    while(!q.empty())
        q.pop();
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            if(d[i][j] != inf && i != j)
                adde(i,j,d[i][j] * mid - pro[i][j]);
    for(int i = 1;i <= n;i++)
        q.push(i), dis[i] = 0, vis[i] = 1, cnt[i] = 1;
    while(!q.empty()) {
        int u = q.front();  q.pop();  vis[u] = 0;     
        if(cnt[u] > n)
            return 1;
        for(int i = hd[u];~i;i = nxt[i]) {
            int v = ver[i];  db w = wei[i];   
            if(dis[u] + w < dis[v]) {
                dis[v] = dis[u] + w;  
                if(!vis[v]) {
                    vis[v] = 1;
                    cnt[v]++;
                    q.push(v);
                }
            }
        }
    }
    return 0;      
}

int main() {
    read(n);  read(m);  read(K);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= K;j++)
            read(B[i][j]), read(S[i][j]);  K++;    
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)   
            for(int k = 1;k <= K;k++)
                if(~S[j][k] && ~B[i][k])
                    cmax(pro[i][j],S[j][k] - B[i][k]);     
    memset(d,0x3f,sizeof(d));  
    for(int i = 1, u, v, w;i <= m;i++) {
        read(u);  read(v);  read(w);
        d[u][v] = w;   
    }
    for(int i = 1;i <= n;i++)
        d[i][i] = 0;
    for(int k = 1;k <= n;k++)
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
                cmin(d[i][j],d[i][k] + d[k][j]);    
    db l = 0, r = 1e9;      
    while(!equal(l,r)) {
        db mid = (l + r) / 2.0;   
        if(check(mid))
            l = mid;
        else
            r = mid;
    }
    printf("%d\n",(int)(floor(r)));
    return 0;  
}

转载于:https://www.cnblogs.com/Sai0511/p/11264122.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值