[APIO2017]商旅(分数规划,spfa)

题面

luogu

题意

n点m边有向图,k种商品,每种商品在每个点有一个售价和收购价
(可能存在点不支持某种商品的买卖)
同一时刻只能携带一种商品,求一个回路使得收益最大
答案向下取整
\(1 \leq N \leq 100,1 \leq M \leq 9900, 1 \leq K \leq 1000\)

题解

12opts很简单 处理出每个点到1的最短路,最大收益和从1到达的最短路即可
开始想的是分数规划+无源汇最大费用最大流check
两点之间的边就是在这两点做购入售出的最大收益减去mid * dis
然后发现这个无源汇最大费用最大流要求为正数还要成环感觉没法做怎么又这么熟悉
于是用spfa判负环就好了。。

#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#define Dis(x, y, z) (w[x][y] - road[x][y] * z)
using namespace std;
const int N = 105;
const int K = 1005;
const long long inf = 1e18;
int n, m, k;
long long road[N][N], w[N][N];
int b[N][K], s[N][K];
inline int getmx(int x, int y){
    int res = 0;
    for(int i = 1; i <= k; ++i) if(~s[y][i] && ~b[x][i]) res = max(res, s[y][i] - b[x][i]);
    return res;
}
queue<int> que; 
bool vis[N];
int cnt[N]; long long dis[N];
inline bool check(int W){
    //printf("W = %d\n", W);
    memset(dis, 0, sizeof(dis));
    while(!que.empty()) que.pop();
    for(int i = 1; i <= n; ++i) que.push(i), vis[i] = 1, cnt[i] = 0;
    while(!que.empty()){
        int fro = que.front(); que.pop(); vis[fro] = 0;
        if(++cnt[fro] > n) return 1;
        for(int i = 1; i <= n; ++i) if(fro != i){
            //if(road[fro][i] < inf && W < 5) printf("%d %d %lld %lld\n", fro, i, dis[i], road[fro][i] * W - w[fro][i]);
            if(road[fro][i] < inf && dis[i] >= dis[fro] + (road[fro][i] * W - w[fro][i])){
                dis[i] = dis[fro] + (road[fro][i] * W - w[fro][i]);
                if(!vis[i]) que.push(i), vis[i] = 1;
            }
        }
    }
    //printf("Yeah!\n");
    return 0;
} 
int main(){
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= k; ++j)
            scanf("%d%d", &b[i][j], &s[i][j]);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j) road[i][j] = inf;
        road[i][i] = 0;
    }
    for(int i = 1, x, y, z; i <= m; ++i)
        scanf("%d%d%d", &x, &y, &z), road[x][y] = z;
    for(int p = 1; p <= n; ++p)
        for(int i = 1; i <= n; ++i) if(i != p)
            for(int j = 1; j <= n; ++j) if(j != i && j != p){
                road[i][j] = min(road[i][j], road[i][p] + road[p][j]);  
            }
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            w[i][j] = getmx(i, j);
    int l = 0, r = 1e9, mid, ret;
    while(l < r){
        mid = l + ((r - l + 1) >> 1);
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    printf("%d\n", l);
    return 0;
}

转载于:https://www.cnblogs.com/hjmmm/p/10808785.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值