POJ2728 Desert King 01规划/最小比率生成树+二分+最小生成树

有了上道题的基础,这题并不难,,但是一开始TLE了,后来一直WA。。。

TLE是因为二分的时间复杂度还是有点大,但稍微优化一下就卡过了。

先附上TLE的代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;

const int MAX=1005;
int n;
double dl[MAX][MAX],dh[MAX][MAX];
double wei[MAX][MAX];
struct point
{
    int x,y,z;
}p[MAX];
double prim()
{
    double dis[MAX];
    bool vis[MAX];
    memset(vis,false,sizeof(vis));
    memset(dis,INF,sizeof(dis));
    int k=0;
    dis[k]=0;vis[k]=true;
    for(int i=1;i<n;i++)
    {
        for(int j=0;j<n;j++)
            if(!vis[j]&&dis[j]>wei[k][j])
                dis[j]=wei[k][j];
        int mi=INF;
        for(int j=0;j<n;j++)
            if(!vis[j]&&dis[j]<mi)
            {
                mi=dis[j];
                k=j;
            }
        vis[k]=true;
    }
    double ans=0;
    for(int i=0;i<n;i++)
        ans+=dis[i];
    return ans;
}
bool judge(double mid)
{
    memset(wei,INF,sizeof(wei));
    for(int i=0;i<n-1;i++)
    {
        for(int j=i+1;j<n;j++)
        {
            wei[i][j]=wei[j][i]=dh[i][j]-mid*dl[i][j];
        }
    }
    double ans=prim();
    if(ans<=0)//mid偏大
        return true;
    else
        return false;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)
            break;
        for(int i=0;i<n;i++)
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
        memset(dl,INF,sizeof(dl));
        memset(dh,INF,sizeof(dh));
        for(int i=0;i<n-1;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                dl[i][j]=dl[j][i]=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
                dh[i][j]=dh[j][i]=fabs(p[i].z-p[j].z);
            }
        }
        double l=0,r=10000000;
        while(r-l>1e-6)
        {
            double mid=(l+r)/2;
            if(judge(mid))
                r=mid;
            else
                l=mid;
        }
        printf("%.3f\n",l);
    }
    return 0;
}

后来发现上界r是可以变小一些的,然后再优化一些部分,终于AC了。。

需要注意dis数组在更新的时候,要把mid值利用进去,不然会出错。附上AC代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;

const int MAX=1005;
int n;
double dl[MAX][MAX],dh[MAX][MAX];
double wei[MAX][MAX];
struct point
{
    double x,y,z;
}p[MAX];
double prim(double mid)
{
    double dis[MAX];
    bool vis[MAX];
    double ans=0;
    memset(vis,false,sizeof(vis));
    int k=0;
    vis[k]=true;
    for(int i=1;i<n;i++)
        dis[i]=dh[0][i]-dl[0][i]*mid;
    for(int i=1;i<n;i++)
    {
        double mi=INF;//注意是double!!!
        for(int j=1;j<n;j++)
            if(!vis[j]&&mi>dis[j])
            {
                k=j;
                mi=dis[j];
            }
        vis[k]=true;
        ans+=mi;
        for(int j=1;j<n;j++)
            if(!vis[j]&&dis[j]>dh[k][j]-dl[k][j]*mid)
                dis[j]=dh[k][j]-dl[k][j]*mid;
    }
    if(ans<0)//mid偏大
        return true;
    else
        return false;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)
            break;
        for(int i=0;i<n;i++)
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        memset(dl,0.0,sizeof(dl));
        memset(dh,0.0,sizeof(dh));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                dl[i][j]=dl[j][i]=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
                dh[i][j]=dh[j][i]=fabs(p[i].z-p[j].z);
            }
        }
        double l=0.0,r=1000.0;
        while(r-l>1e-5)
        {
            double mid=(l+r)/2;
            if(prim(mid))
                r=mid;
            else
                l=mid;
        }
        printf("%.3f\n",l);
    }
}

这道题很迷的一直卡一直卡,最后我也没明白之前WA在哪儿了。。

还有一种迭代写法,我试了下,但是没弄成,这道题实在是不想看了,就先放这儿吧。。。

附上迭代写法的大佬博客:https://blog.csdn.net/zhang20072844/article/details/8176854

但是好像复制粘贴后没跑通???emmmmmm…

点赞