原题链接
考察:二分+SPFA
引入:形如 \({\sum_1^n f[i] \over \sum_1^n g[j]}\)求其最大值,称为01分数规划问题.通过二分最大值,检验解的存在性,这样的存在性具有单调性,所以可以用二分解决.
思路:
想到二分之后,我们可以假设最大值 = mid,对于每一个环,检验\({\sum_1^n f[i] \over \sum_1^n t[i]}\) >=mid 如果为true,就调大mid,否则mid变小.
如果寻找每一个环,时间复杂度应该是O(n2m*log2m)铁TLE,所以我们需要考虑优化.
首先必须整理式子,\(\sum_1^n f[i]\) >= mid*\(\sum_1^n t[i]\) 而这个等价于 \(\sum_1^n (f[i]-mid*t[i])\) 这个问题又等价于我们需要找到一个环,环的边权与点权符合上面的性质.此时如果我们把边的权值变成f[i] - mid*t[i]就转化为是否能在图中找到正环.
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1010,M = 5010;
const double eps = 1e-6;
int n,h[N],m,score[N],idx,cnt[N];
bool st[N];
double dist[N];
struct Road{
int fr,to,ne;
double w;
Road operator=(const Road& r){
this->fr = r.fr;
this->to = r.to;
this->ne = r.ne;
this->w = r.w;
return *this;
}
}road[M],path[M];
void add(int a,int b,double c)
{
road[idx].w = c,road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
}
bool spfa()
{
queue<int> q;
for(int i=1;i<=n;i++)
{
st[i] = 1;
dist[i] = cnt[i] = 0;
q.push(i);
}
while(q.size())
{
int u = q.front();
q.pop();
st[u] = 0;
for(int i=h[u];~i;i=road[i].ne)
{
int v = road[i].to;
if(dist[v]<dist[u]+road[i].w)
{
dist[v] = dist[u]+road[i].w;
cnt[v] = cnt[u]+1;
if(cnt[v]>=n) return 1;
if(!st[v]) q.push(v),st[v] = 1;
}
}
}
return 0;
}
bool check(double mid)
{
for(int i=0;i<idx;i++) road[i] = path[i];
for(int i=0;i<idx;i++)
{
int v = road[i].to;
road[i].w = score[v] - mid*road[i].w;
}
if(spfa()) return 1;
else return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&score[i]),h[i] = -1;
while(m--)
{
int a,b; double w;
scanf("%d%d%lf",&a,&b,&w);
add(a,b,w);
}
for(int i=0;i<idx;i++) path[i] = road[i];
double l = 0,r = 1010;
while(r-l>=eps)
{
double mid = (l+r)/2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.2lf\n",r);
return 0;
}