求sum(fi)/sum(ti)的最大值问题叫做01分数规划问题。
做法:
1.求sum(fi)/sum(ti)的取值范围[L,R]
2.二分 整理sum(fi)/sum(ti)>mid 重新确定点权和边权
例题1:https://vjudge.net/problem/POJ-3621
sum(fi)/sum(ti)>mid
sum(fi)-sum(ti)mid>0
sum(fi-midti)>0
把点的边权变成(fi-mid*ti)
题目变成在这样一个图中是否存在正环(spfa 最长路求正环)
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int n,m;
const int N=1010,M=5050;
int head[N],ne[M],ver[M],edge[M],tot;
double dist[N];int st[N],cnt[N];
int f[N];
void add(int a,int b,int c)
{
edge[tot]=c;
ver[tot]=b;
ne[tot]=head[a];
head[a]=tot++;
}
bool check(double mid)
{
memset(dist,0,sizeof dist);
memset(st,0,sizeof st);
memset(cnt,0,sizeof cnt);
queue<int>q;
for(int i=1;i<=n;i++)
{
q.push(i);
st[i]=1;
}
while(q.size())
{
int x=q.front();
q.pop();
st[x]=0;
for(int i=head[x];i!=-1;i=ne[i])
{
int y=ver[i];
if(dist[y]<dist[x]+f[x]-mid*edge[i])
{
dist[y]=dist[x]+f[x]-mid*edge[i];
cnt[y]=cnt[x]+1;
if(cnt[y]>=n)
{
return 1;
}
if(!st[y])
{
q.push(y);
st[y]=1;
}
}
}
}
return 0;
}
int main()
{
memset(head,-1,sizeof head);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&f[i]);
}
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
double l=1,r=1000;
while(r-l>1e-4)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.2lf\n",l);
}
2万+

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



