bzoj4898 & loj2308 [Apio2017]商旅 最短路+01分数规划

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4898

https://loj.ac/problem/2308

题解

发现我们可以把整个环路分成很多段,每一段都携带着一个物品。

那么从 \(x\)\(y\) 的这一段,我们可以预处理出应该选择什么物品。可以发现这个是不会变化的。

于是我们可以视为问题转化为了这样一个问题:给定一个有向完全图,每一条边有一个价值 \(w\),还有一个费用 \(t\),选择一个环,使得 \(\frac{\sum w}{\sum t}\) 最大。

这显然是一个分数规划的模型,于是直接二分,每条边的边权是一个 \(w-mid\cdot t\),只需要判断有没有非负的环就可以了,可以使用 \(spfa\)


代码如下,时间复杂度为 \(nm\log W\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 100 + 7;
const int M = 10000 + 7;
const int K = 1000 + 7;
const int INF = 0x3f3f3f3f;
const ll INF_ll = 0x3f3f3f3f3f3f3f3f;

int n, m, k, maxw, hd, tl;
int b[N][K], s[N][K]; // b = buy, s = sell
int f[N][N], w[N][N];
int num[N], q[N], inq[N];
ll dis[N], g[N][N];

inline void floyd() {
	for (int k = 1; k <= n; ++k)
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
				if (i != j) smin(f[i][j], f[i][k] + f[k][j]);
}

inline void ycl() {
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			for (int l = 1; l <= k; ++l) if (~s[j][l] && ~b[i][l]) smax(w[i][j], s[j][l] - b[i][l]);
}

inline void qpush(int x) { ++tl; tl == N ? tl = 1 : 0; q[tl] = x; }
inline int qhead() { ++hd; hd == N ? hd = 1 : 0; return q[hd]; }

inline bool spfa() {
	hd = tl = 0;
	for (int i = 1; i <= n; ++i) dis[i] = -INF_ll, inq[i] = 0, num[i] = 0;
	dis[1] = 0, qpush(1), inq[1] = 1;
	while (hd != tl) {
		int x = qhead();
		inq[x] = 0;
		for (int y = 1; y <= n; ++y) if (y != x && dis[y] <= dis[x] + g[x][y]) {
			dis[y] = dis[x] + g[x][y], num[y] = num[x] + 1;
			if (num[y] >= n) return 1;
			if (!inq[y]) qpush(y), inq[y] = 1;
		}
	}
	return 0;
}

inline bool check(const int &mid) {
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			g[i][j] = w[i][j] - (ll)mid * f[i][j];
	return spfa();
}

inline void work() {
	floyd();
	ycl();
	int l = 0, r = maxw;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	printf("%d\n", l);
}

inline void init() {
	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]);
	int x, y, z;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			if (i == j) f[i][j] = 0;
			else f[i][j] = INF;
	for (int i = 1; i <= m; ++i) read(x), read(y), read(z), smin(f[x][y], z), smax(maxw, z);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-09-19 19:30  hankeke303  阅读(174)  评论(0编辑  收藏  举报