【题目链接】
【思路要点】
- 明显的分数规划,第一步先二分答案\(Ans\)。
- 接下来,有一种显然的想法是拆点,把在每一个点时,持有某个物品的状态拆成\(K\)个点,然后,问题转化为了图中是否存在非负环。
- 但这个做法复杂度是\(O((N+M)KLogS)\)的,(应该)无法通过。
- 进一步考虑,实际上,我们并不需要记录当前持有什么物品,如果一次买卖的买入市场和卖出市场都已经确定,那么这次买卖的最大获利是可以在\(O(K)\)的时间内计算的(如果没有可以盈利的购买方案,记最大获利为0)。
- 记从点\(i\)到点\(j\)的最短路长度为\(cost_{i,j}\),确定一次买卖的买入市场和卖出市场分别为\(i\),\(j\)时的最大获利为\(gain_{i,j}\),那么我们只要在\((i,j)\)建立权值为\(gain_{i,j}-Ans*cost_{i,j}\)的边,判断图中是否存在非负环即可。
- 时间复杂度可近似认为为\(O(N^3+N^2K+N^2LogS)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; const int MAXK = 1005; const int INF = 1e9; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, k, now; int b[MAXN][MAXK], s[MAXN][MAXK]; int gain[MAXN][MAXN]; int cost[MAXN][MAXN]; long long dist[MAXN]; bool inq[MAXN], found; void work(int pos) { if (inq[pos]) found = true; if (found) return; inq[pos] = true; for (int i = 1; i <= n; i++) if (i != pos && dist[pos] + gain[pos][i] - 1ll * now * cost[pos][i] >= dist[i]) { dist[i] = dist[pos] + gain[pos][i] - 1ll * now * cost[pos][i]; work(i); } inq[pos] = false; } bool check() { found = false; memset(dist, 0, sizeof(dist)); for (int i = 1; i <= n; i++) work(i); return found; } int main() { read(n), read(m), read(k); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i == j) cost[i][j] = 0; else cost[i][j] = INF; for (int i = 1; i <= n; i++) for (int j = 1; j <= k; j++) read(b[i][j]), read(s[i][j]); for (int i = 1; i <= m; i++) { int x, y, z; read(x), read(y), read(z); chkmin(cost[x][y], z); } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) chkmin(cost[j][k], cost[j][i] + cost[i][k]); int l = 0, r = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { int res = 0; for (int l = 1; l <= k; l++) if (b[i][l] != -1 && s[j][l] != -1) chkmax(res, s[j][l] - b[i][l]); gain[i][j] = res; chkmax(r, res); } while (l < r) { now = (l + r + 1) / 2; if (check()) l = now; else r = now - 1; } writeln(l); return 0; }