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 }

 

posted @ 2018-01-19 17:20  大奕哥&VANE  阅读(131)  评论(0编辑  收藏  举报