题意:给n个点,m条A边,k条B边。要求从某一个点出发连续走过若干个B边再连续走若干条A边回到起点。使得A边的权值之和与B边的权值之和之比最大。
思路:要想使得比例最大,肯定要使得A边权值之和尽可能的大,B边权值之和尽可能的小。对于给定的图,我们分别对A边和B边建立两种图,然后枚举起点,A图中用SPFA跑一遍最长路,B图中跑一遍最短路,再枚举转点,即可更新答案。
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define N 1004
struct E {
int v, d, ne, id;
E() {}
E(int _v, int _d, int _ne, int _id):v(_v), d(_d), ne(_ne), id(_id){}
}e[N*2];
int size, head[N], dis[2][N][N], flag[N], ca = 1, pre[2][N][N];
int ans[N*2];
double re;
queue<int>Q;
void init() {
size = 0;
memset(dis, -1, sizeof(dis));
memset(head, -1, sizeof(head));
}
void add(int u, int v, int d, int id) {
e[size] = E(v, d, head[u], id);
head[u] = size++;
}
void spfa(int u, int id) {
int i, v, d;
ca++;
while (!Q.empty()) Q.pop();
Q.push(u), flag[u] = ca, dis[id][u][u] = 0;
while (!Q.empty()) {
v = Q.front();
Q.pop(), flag[v] = ca-1;
for (i = head[v];~i;i = e[i].ne) {
if (e[i].id != id) continue;
int vv = e[i].v;
int *tm = &dis[id][u][vv];
d = e[i].d;
if (id && ((*tm) == -1 || (*tm) < dis[id][u][v]+d)) {
(*tm) = dis[id][u][v]+d;
pre[id][u][vv] = v;
if (flag[vv] != ca) {
flag[vv] = ca, Q.push(vv);
}
}
if (!id && ((*tm) == -1 || (*tm) > dis[id][u][v]+d)) {
(*tm) = dis[id][u][v]+d;
pre[id][u][vv] = v;
if (flag[vv] != ca) {
flag[vv] = ca, Q.push(vv);
}
}
}
}
}
int main() {
int T, n, m, k, i, u, v, d, j;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &k);
init();
for (i = 0;i < m;i++) {
scanf("%d%d%d", &u, &v, &d);
add(v, u, d, 1);
}
for (i = 0;i < k;i++) {
scanf("%d%d%d", &u, &v, &d);
add(u, v, d, 0);
}
re = 0;
for (i = 1;i <= n;i++) {
spfa(i, 1), spfa(i, 0);
for (j = 1;j <= n;j++) {
if (j == i || dis[0][i][j] == -1 || dis[1][i][j] == -1) continue;
double tm = dis[1][i][j]*1.0/dis[0][i][j];
if (tm > re) re = tm, u = i, v = j;
}
}
printf("%d ", u);
i = v, j = 0;
while (i != u) {
ans[j++] = i;
i = pre[0][u][i];
}
for (i = j-1;i >= 0;i--) printf("%d ", ans[i]);
while (pre[1][u][v] != u) printf("%d ", pre[1][u][v]), v = pre[1][u][v];
printf("%d\n%.3lf\n", u, re);
}
}