[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;
}
posted @ 2019-05-04 17:54  hjmmm  阅读(98)  评论(0编辑  收藏  举报