1486: [HNOI2009]最小圈
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1608 Solved: 752
[Submit][Status][Discuss]
Description
题解:0-1分数规划。
分数规划一般是来解决这样一种问题的:
ans=∑ai∗xi∑bi∗xi
其中x∈{0,1},b∈R+我们要求一个ans的最小值或者最大值。
假设我们要求一个最小值
首先我们可以先将式子变形成这样:
∑ai∗xi=ans∗∑bi∗xi
∑ai∗xi−ans∗∑bi∗xi=0
然后很容易我们可以看出来这个关于ans的函数是一个线性的,所以可以二分。
假设我们二分到一个ans1,如果ans比ans1要更优也就是要更小,也就是
∑ai∗xi−ans1∗∑bi∗xi<0
这样然后我们就可以根据二分出来的答案去验证一下,最后求出我们所要求的ans。最大值也一样。
然后这道题就比较简单了,我们根据上面的过程可以把要求的的东西变成
∑i=1kw[i][i+1]−ans∗k=0
也是可以先二分答案,那么我们如果将每条边的边权改成w[i][i+1]-ans,这样我们从这张图上来跑最短路,如果找到了一个复权环,就说明我么当前的ans比较大,不是最优的,所以可以再取更小的值
这样用二分+SPFA判复权环就可以了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define exp 1e-10
#define mid (l+r)/2
#define inf 1000000000
const int N=3010;
const int M=20010;
double dis[N];
bool f[N],flag;
int n,m,tot,point[N],next[M];
struct S{int st,en;double va;}aa[M],e[M];
inline void add(int x,int y,double z){
tot+=1;next[tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
}
inline void SPFA(int x){
int i;
f[x]=false;
for(i=point[x];i;i=next[i])
if(dis[aa[i].en]>dis[x]+aa[i].va)
if(!f[aa[i].en]){
flag=true;
break;
}
else{
dis[aa[i].en]=dis[x]+aa[i].va;
SPFA(aa[i].en);
}
f[x]=true;
}
inline bool check(double x){
int i,j;
tot=0;
memset(next,0,sizeof(next));
memset(point,0,sizeof(point));
for(i=1;i<=m;++i) add(e[i].st,e[i].en,e[i].va-x);
memset(f,1,sizeof(f));
memset(dis,0,sizeof(dis));
flag=false;
for(i=1;i<=n;++i){
SPFA(i);
if(flag) return true;
}
return false;
}
int main(){
int i,j;
double l=-inf,r=inf;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i) scanf("%d%d%lf",&e[i].st,&e[i].en,&e[i].va);
while(l+exp<r){
if(check(mid)) r=mid;
else l=mid;
}
printf("%.8f\n",l);
}