POJ2728 Desert King
01分数规划。
我们知道01分数规划有两种解法,一种是二分,一种是迭代。
先讲讲思路。
R=sigma(a[i]*x[i])/sigma(b[i]*x[i]),F(L):=sigma(a[i]*x[i])-L*sigma(b[i]*x[i])=sigma(d[i]*x[i]);d[i]=a[i]-b[i]*L
求R的最大值,即F[L]取最小,那么就在当前L取值下跑最大生成树,使得F[L]尽量大那么也就是说有解比当前更优
所以当且仅当F[L]取到0时L=R,此时R取到最大值
本题是求最小值,所以可以将上面取值取倒数,也可以跑最小生成树求解
所以我们可以看出01分数规划的判定不在于你怎样二分判断,而在于你所构造的限制条件,求解最大值就要取大,反之取小。
下面给出二分代码,2300ms,稠密图要用Prim
1 #include<cmath> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 const int N=1e3+10; 11 int n,cnt,head[N];double eps=1e-9; 12 double dis[N][N],hei[N][N],M[N][N]; 13 struct node 14 { 15 double x,y,h; 16 }e[N]; 17 double manh(node a,node b){ 18 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 19 } 20 double low[N];bool v[N]; 21 double prim(double r) 22 { 23 for(int i=1;i<=n;++i)v[i]=0,low[i]=M[i][1]; 24 v[1]=1;double ans=0;double mi;int k; 25 for(int i=2;i<=n;++i) 26 { 27 mi=-1e9;k=1; 28 for(int j=1;j<=n;++j) 29 { 30 if(!v[j]&&mi<low[j]) 31 { 32 k=j;mi=low[j]; 33 } 34 } 35 if(mi==-1e9)break; 36 v[k]=1;ans+=mi; 37 for(int j=1;j<=n;++j) 38 { 39 if(!v[j])low[j]=max(low[j],M[k][j]); 40 } 41 } 42 return ans; 43 } 44 double check(double r) 45 { 46 for(int i=1;i<=n;++i) 47 for(int j=i+1;j<=n;++j) 48 { 49 M[j][i]=M[i][j]=hei[i][j]-r*dis[i][j]; 50 } 51 return prim(r); 52 } 53 int main() 54 { 55 while(~scanf("%d",&n)&&n) 56 { 57 for(int i=1;i<=n;++i) 58 { 59 scanf("%lf%lf%lf",&e[i].x,&e[i].y,&e[i].h); 60 } 61 double l=0,r=0; 62 for(int i=1;i<=n;++i) 63 for(int j=i+1;j<=n;++j) 64 dis[i][j]=dis[j][i]=fabs(e[i].h-e[j].h), 65 hei[i][j]=hei[j][i]=manh(e[i],e[j]), 66 r=max(r,hei[i][j]/dis[i][j]); 67 while(r-l>=eps) 68 { 69 double mid=(l+r)/2; 70 if(check(mid)>=0)l=mid; 71 else r=mid; 72 } 73 printf("%.3f\n",1/l); 74 } 75 return 0; 76 }
迭代类似于我们之前的爬山算法,就是先随机找出一个解,然后朝着那个方向计算,区别在于prim的返回值。
1 #include<cmath> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 const int N=1e3+10; 11 int n,pre[N];double eps=1e-8; 12 double suma=0,sumb=0; 13 double dis[N][N],hei[N][N],M[N][N]; 14 struct node 15 { 16 double x,y,h; 17 }e[N]; 18 double manh(node a,node b){ 19 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 20 } 21 double low[N];bool v[N]; 22 double prim(double r) 23 { 24 for(int i=1;i<=n;++i) 25 v[i]=0,low[i]=M[i][1],pre[i]=1; 26 v[1]=1;double mi;int k; 27 suma=sumb=0; 28 for(int i=2;i<=n;++i) 29 { 30 mi=1e9;k=1; 31 for(int j=1;j<=n;++j) 32 { 33 if(!v[j]&&mi>low[j]) 34 { 35 k=j;mi=low[j]; 36 } 37 } 38 v[k]=1; 39 suma+=dis[pre[k]][k]; 40 sumb+=hei[pre[k]][k]; 41 if(mi==1e9)break; 42 for(int j=1;j<=n;++j) 43 { 44 if(!v[j]&&low[j]>M[k][j]) 45 low[j]=M[k][j],pre[j]=k; 46 } 47 } 48 return suma/sumb; 49 } 50 double check(double r) 51 { 52 for(int i=1;i<=n;++i) 53 for(int j=i+1;j<=n;++j) 54 { 55 M[j][i]=M[i][j]=dis[i][j]-r*hei[i][j]; 56 } 57 return prim(r); 58 } 59 int main() 60 { 61 while(~scanf("%d",&n)&&n) 62 { 63 for(int i=1;i<=n;++i) 64 { 65 scanf("%lf%lf%lf",&e[i].x,&e[i].y,&e[i].h); 66 } 67 for(int i=1;i<=n;++i) 68 for(int j=i+1;j<=n;++j) 69 dis[i][j]=dis[j][i]=fabs(e[i].h-e[j].h),hei[i][j]=hei[j][i]=manh(e[i],e[j]); 70 for(int i=2;i<=n;++i)suma+=dis[1][i],sumb+=hei[i][1]; 71 double x=suma/sumb,x_; 72 while(1) 73 { 74 x_=x; 75 x=check(x); 76 if(fabs(x-x_)<eps)break; 77 } 78 printf("%.3f\n",x); 79 } 80 return 0; 81 }
生命中真正重要的不是你遭遇了什么,而是你记住了哪些事,又是如何铭记的。