题意:给n个点的点权以及m条有向边,求图中一个 点权和与边权和之比 最大的环的 点权和与边权和之比。
思路:与环有关的可以想到SPFA,
设点权为c,i到j的边权为w[i][j],
设答案为ans,点1~p依次连成所求环, 则,
(c[1] + c[2] + ... + c[p]) / (w[1][2] + w[2][3] + ... + w[p][1]) = ans;
ans不好直接求,考虑二分答案,若当前判断是否存在 点权和与边权和之比 > t,
则重新定义从 i 到 j 边权为: w[i][j] * t - c[j],
此时若存在负权环,则考虑这个环的边权之和,
(w[1][2] + w[2][3]... + w[p][1]) * t - (c[1] ... + c[p]) < 0
t < (c[1] ... + c[p]) / (w[1][2] + w[2][3] ... + w[p][1]) = ans, 即答案一定比 t 大,
若不存在反之。
注意最好用%f输出。。。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <vector>
#define For(i,j,k) for(int i = j;i <= (int)k;i ++)
#define Set(i,j) memset(i, j, sizeof(i))
#define pb push_back
using namespace std;
const int N = 1010;
vector<int> G[N], w[N];
int n, m, c[N], inq[N];
double d[N];
void Add(int x, int y, int z){
G[x].pb(y), w[x].pb(z);
}
bool SPFA(double val){
queue<int> q;
For(i,0,n) d[i] = 1e18;
Set(inq, 0);
inq[1] = -1, d[1] = 0, q.push(1);
while(!q.empty()){
int s = q.front(); q.pop();
inq[s] = -inq[s];
For(i,0,G[s].size() - 1){
int v = G[s][i];
double dis = val * w[s][i] - c[v];
if(dis + d[s] < d[v]){
d[v] = dis + d[s];
if(inq[v] >= 0){
if(inq[v] >= n) return 1;
inq[v] = -inq[v] - 1;
q.push(v);
}
}
}
}
return 0;
}
int main(){
scanf("%d%d", &n, &m);
For(i,1,n) scanf("%d", &c[i]);
For(i,1,m){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
Add(x, y, z);
}
double L = 0, R = N, M;
while(R - L > 1e-4){
M = (L + R) / 2;
if(SPFA(M)) L = M;
else R = M;
}
printf("%.2f", M);
return 0;
}
本文介绍一种利用SPFA算法结合二分查找求解图中具有最大点权和与边权和之比的环的方法。通过修改边权并搜索负权环来逼近最优解。
932

被折叠的 条评论
为什么被折叠?



