poj2728 最小比率生成树——01分数规划
题目大意:
有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,
只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,
现在要求方案使得费用与距离的比值最小,很显然,这个题目是要求一棵最优比率生成树。
————————————————————————————————————
这是一道最优比率生成树的题目,是个很明显的0-1分数规划,设每条边代价为ci,距离为di
那么题目要求(∑(ci*xi))/(∑(di*xi))的最小值 xi∈{0,1}
我们进行一波转换
z=(∑(ci*xi))-r'*(∑(di*xi)),其中z是左边这个式子的最小值
由于di为正数,xi为非负数,所以
r'>r 时 z(r')<0
r'=r 时 z(r')=0
r'<r 时 z(r')>0
那么二分这个最小值,将这个式子化成xi(ci-r'*di)的形式,每条边的权值变成ci-r'*di
对于这些边,求一棵最小生成树,MST的值即为z(r')
这样问题就解决了QAQ(注:! 这里输出要%.3f 不能lf !!! 我错了五次就在这里
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int M=1e4+7; const double inf=1e15; int n; int v[M][M]; int x[M],y[M],w[M],vis[M]; double d[M],map[M][M]; double calc(int s1,int s2){return sqrt(1.0*(x[s1]-x[s2])*(x[s1]-x[s2])+1.0*(y[s1]-y[s2])*(y[s1]-y[s2]));} double prim(double k){ double sum=0; memset(vis,0,sizeof(vis)); d[1]=0; vis[1]=1; for(int i=2;i<=n;i++) d[i]=(double)v[1][i]-k*map[1][i]; for(int i=2;i<=n;i++){ double mn=inf; int h=0; for(int j=2;j<=n;j++) if(!vis[j]&&mn>d[j]) mn=d[j],h=j; sum+=mn; d[h]=0; vis[h]=1; for(int j=2;j<=n;j++) if(!vis[j]&&((double)v[h][j]-k*map[h][j])<d[j]) d[j]=(double)v[h][j]-k*map[h][j]; } return sum; } int main() { while((scanf("%d",&n)!=EOF)&&n){ for(int i=1;i<=n;i++) scanf("%d %d %d",&x[i],&y[i],&w[i]); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) map[i][j]=map[j][i]=calc(i,j),v[i][j]=v[j][i]=abs(w[i]-w[j]); double l=0.0,r=100000.0; while(r-l>1e-7){ double mid=(l+r)/2; if(prim(mid)>=0) l=mid; else r=mid; }printf("%.3f\n",r); } return 0; }