标签:
题意:
给出n(<=200)个点,m(<=2000)条边的有向无环图,每条边有个边权,问从1号节点到n号节点的路径上的 权值和 与 点数的最小比值
题解:
这个题有两种做法:
一、
定义dis[u][k]表示从1号节点到达u号点经过k个节点的最小权值和,用SPFA就可以完成,最后直接循环找一遍dis[n][k] / k的最小值
二、
但是以上只适用于点很少的情况,点多的情况一般采用以下套路~
另建一个起点0,连接一条0到1长度为0的边,就此将问题转化为长度和边数最小比值。这个问题的求解需要分数规划。
假设答案为ans,对于任意一条由k条边组成的路径,有:
由 (w1 + w2 + w3 + … + wk) / k >= ans
可以得到 : (w1 + w2 + w3 + … + wk) >= ans * k
即(w1 - ans) + (w2 - ans) + (w3 - ans) + … + (wk - ans) >= 0
于是就很容易想到
二分答案x,每次将每一条边的权值减去x求最短路,判断1~n的最短路是否大于0
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 2e2 + 7;
const int M = 2e3 + 7;
const double eps = 1e-9;
int head[N], n, m, ecnt, inq[N];
double dis[N];
struct edge {
int v,nxt;
double w;
}e[M];
void adde (int u, int v, int w) {
e[ecnt].v = v;
e[ecnt].w = w;
e[ecnt].nxt = head[u];
head[u] = ecnt++;
}
int check (double x) {
for (int i = 0; i < ecnt; ++i) e[i].w -= x;
for (int i = 1; i <= n; ++i) dis[i] = 1e9 + 7, inq[i] = 0;
queue <int> q;
q.push(0), dis[0] = 0, inq[0] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
for (int it = head[u]; it != -1; it = e[it].nxt) {
int v = e[it].v;
if (dis[v] > dis[u] + e[it].w) {
dis[v] = dis[u] + e[it].w;
if (!inq[v]) {
q.push(v);
inq[v] = 1;
}
}
}
}
for (int i = 0; i < ecnt; ++i) e[i].w += x;
return dis[n] - eps < 0;
}
int main () {
scanf ("%d%d", &n, &m);
memset (head, -1, sizeof head);
adde (0, 1, 0);
for (int i = 1; i <= m; ++i) {
int u, v, w;
scanf ("%d%d%d", &u, &v, &w);
adde (u, v, w);
}
double l = 0, r = M;
while (l + eps < r) {
double mid = (l + r) / 2.0;
if (check (mid)) r = mid;
else l = mid;
}
printf ("%.3lf\n", l);
return 0;
}
标签:
原文地址:http://www.cnblogs.com/xgtao/p/5964883.html