zoukankan      html  css  js  c++  java
  • [USACO18OPEN]Talent Show 解题报告

    题目:传送门(洛谷)

    这道题可谓刷新了我对0/1分数划分的认识。

    之前做过一道最小生成树+0/1分数划分(在我的博客里也有),但这次写的时候没联想到背包问题,考场(noip模拟)上直接上了遗传然后老师多次测评取最低值就只12分了qwq。

    如果对0/1分数划分不熟悉的读者可以戳这里阅读相关文章(我觉得我写得还不错(雾))

    首先不难发现我们在标操之后,要选出一些牛,这些牛的w(体重)和要大于等于W(体重下限),且他们的评估函数(t-mid*w)之和是否大于等于0决定了mid是大了还是小了(差点写成中考经典病句)。

    而怎么选这些牛,就是一个相对经典的背包问题,dp[v]表示用了v的体重,能收集到的最大评估函数的和。

    套进背包问题的模板里就行了。

    那么代码如下:

    #include<iostream>
    #include<cstdio>
    #define maxn 300
    using namespace std;
    inline void read(int &x)
    {
        x=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    inline void read(double &x)
    {
        x=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    int N,W;
    double w[maxn],t[maxn];
    double c[maxn],dp[1005];
    bool check(double mid)
    {
        for(int i=1;i<=N;i++)
            c[i]=t[i]-mid*w[i];
        for(int i=0;i<=W;i++)
            dp[i]=-0x3f3f3f3f;
        dp[0]=0;
    /*    for(int v=0;v<=W;v++)
        {
            for(int i=1;i<=N;i++)
            {
                int tmp=v+w[i];
                if(tmp>W)
                    tmp=W;
                dp[tmp]=max(dp[tmp],dp[v]+c[i]);
            }
        }*/
        for(int i=1;i<=N;i++)
            for(int v=W;v>=0;v--)
            {
                int tmp=v+w[i];
                if(tmp>W)
                    tmp=W;
                dp[tmp]=max(dp[tmp],dp[v]+c[i]);
            }
    //    cout<<mid<<"   "<<dp[W]<<endl;
        if(dp[W]>=0.0)
            return 1;
        else    
            return 0;
    }
    int main()
    {
        read(N);read(W);
        double tw=0,tt=0;
        for(int i=1;i<=N;i++)
        {
            read(w[i]);
            read(t[i]);
            tw+=w[i];
            tt+=t[i];
        }
        double l=0,r=100005,mid,ans;
        while(r-l>0.00001)
        {
            mid=(r+l)/2;
            //cout<<l<<"   "<<r<<"   ";
            if(check(mid))
                l=mid,ans=mid;
            else
                r=mid;
        }
        ans*=1000;
        printf("%d",int(ans));
    }
    View Code

    --------------------Warning:下面的内容不是正解!!!------------------------

    然后我在考场上写的伪遗传在遗传8000次左右可以保证正确,但是时间会高达10s左右。

    我提交的版本为了保证时间,只遗传了400代(事实证明一秒内我可以遗传800代甚至更多)。

    为了保证时间,我甚至没有写交换。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #define maxn 300
    #define yep cout<<"yep"<<endl;
    using namespace std;
    inline void read(double &x)
    {
        x=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    double N,W;
    double w[maxn],t[maxn];
    struct node{
        int u[maxn];
        double f;
        void cal()
        {
            double wt=0,tt=0;
            for(int i=1;i<=N;i++)
                if(u[i])
                    wt+=w[i],tt+=t[i];
            if(wt<W)
                f=-1;
            else
                f=tt/wt;
        }
        friend bool operator < (node a,node b)
        {
            return a.f>b.f;
        }
    }group[1005];
    inline void getnew(int x)
    {
        for(int i=1;i<=N;i++)
            group[x].u[i]=rand()%2;
        group[x].cal();
    }
    node fuck(node fa,node ma)
    {
        int c;
        node child;
        for(int i=1;i<=N;i++)
        {
            c=rand()%2;
            if(c)
                child.u[i]=fa.u[i];
            else
                child.u[i]=ma.u[i];
        }
        child.cal();
        return child;
    }
    int main()
    {
        freopen("talent.in","r",stdin);
        freopen("talent.out","w",stdout);
        srand(time(NULL));
        read(N);read(W);
        double tw=0,tt=0;
        for(int i=1;i<=N;i++){read(w[i]);read(t[i]);}
        int T=400;
        for(int i=1;i<=T;i++)    
        {
            while(group[i].f<=0)
                getnew(i);
        }
        for(int i=1;i<=T;i++)
        {
            sort(group+1,group+T+1);
            for(int i=T/2+1;i<=T;i++)
                group[i]=fuck(group[i-T/2],group[i-T/2+1]);
        }
        sort(group+1,group+T+1);
        double ans;
        ans=group[1].f;
        ans*=1000;
        printf("%d",int(ans));
    }
    View Code
  • 相关阅读:
    [Luogu P4178]Tree 题解(点分治+平衡树)
    [20190725NOIP模拟测试8]题解
    暑假集训考试反思+其它乱写
    [bzoj2752]高速公路 题解(线段树)
    bzoj1211树的计数 x bzoj1005明明的烦恼 题解(Prufer序列)
    [CQOI2014]数三角形 题解(找规律乱搞)
    [Catalan数三连]网格&有趣的数列&树屋阶梯
    [NOIP模拟测试7]visit 题解(组合数学+CRT+Lucas定理)
    [7.22NOIP模拟测试7]方程的解 题解(扩展欧几里得)
    leetcode371
  • 原文地址:https://www.cnblogs.com/sherrlock/p/9798557.html
Copyright © 2011-2022 走看看