【洛谷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
",(int)(floor(r)));
return 0;
}