• 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
    ",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
    ",x);
    79     }
    80     return 0;
    81 }
  • 相关阅读:
    机器学习笔记
    python学习笔记-day8
    python学习笔记-day7
    python学习笔记-day6
    python学习笔记-day5
    python习题
    単語
    bat批处理----copy和xcopy区别
    C#
    VB
  • 原文地址:https://www.cnblogs.com/nbwzyzngyl/p/8318025.html
Copyright © 2020-2023  润新知