zoukankan      html  css  js  c++  java
  • 洛谷 2831 (NOIp2016) 愤怒的小鸟——仅+1所以bfs优化

    题目:https://www.luogu.org/problemnew/show/P2831

    状压dp。跑得很慢。(n^2*2^n)

    注意只打一只猪的情况。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int Lm=18;const double eps=1e-10;
    int T,n,dp[(1<<Lm)+5],lm,tot,tp,list[Lm*Lm+5];
    struct Node{
        double x,y;
    }r[Lm+5];
    //struct Sit{
    //    int s,cnt;
    //}b[(1<<Lm)+5];
    //bool cmp(Sit u,Sit v){return u.cnt<v.cnt;}
    //int calc(int s)
    //{
    //    int ret=0;while(s)ret++,s-=(s&-s);return ret;
    //}
    int solve(int i,int j)
    {
        double y1=r[i].x,x1=y1*y1,z1=r[i].y;
        double y2=r[j].x,x2=y2*y2,z2=r[j].y;
        double k=x2/x1;y1*=k;z1*=k;y1-=y2;z1-=z2;
        double b=z1/y1,a=(z2-b*y2)/x2;
        if(a>=0)return -1;
        int ret=0;
        for(int p=1;p<=n;p++)
            if(fabs(r[p].x*r[p].x*a+r[p].x*b-r[p].y)<=eps)ret|=(1<<(p-1));
        return ret;
    }
    void init()
    {
    //    for(int i=0;i<lm;i++)b[i+1].s=i,b[i+1].cnt=calc(i);
    //    sort(b+1,b+lm,cmp);
        tot=0;
        for(int i=0;i<n;i++)list[++tot]=(1<<i);///////////
        for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
        {
            list[++tot]=solve(i,j);
            if(list[tot]==-1)tot--;
        }
        memset(dp,1,sizeof dp);dp[0]=0;
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&tp);lm=(1<<n);
            for(int i=1;i<=n;i++)scanf("%lf%lf",&r[i].x,&r[i].y);
            init();
            for(int i=1;i<=tot;i++)
                for(int s=0;s<lm;s++)
                    dp[s|list[i]]=min(dp[s|list[i]],dp[s]+1);
    //        for(int i=1;i<=lm;i++)
    //        {
    //            int s=b[i].s;
    //            for(int j=1;j<=tot;j++)
    //                dp[s|list[j]]=min(dp[s|list[j]],dp[s]+1);
    //        }
            printf("%d
    ",dp[lm-1]);
        }
        return 0;
    }
    View Code

    到处看TJ。发现好像是因为弄了很多冗余状态。

    让我们分析为什么当前S一定要打掉第一只未打的猪。(n*2^n)

      我们可以把 “前缀连续的1多了一个” 看做更进了一步。反正最后要 “前缀连续的1有n个” ,所以这样看也还行。

      那么不打第一只未打的猪的转移就是同层转移了。这种转移不能使我们离最终状态更近。

      但是它可以让后面的0更少之类的,可能对后面有好的影响。

      分析一下这里:如果现在打了后面、后面再补回来现在这个“第一只未打的猪”可以更优的话,因为打猪的顺序无关,所以可以对应成现在打了“第一只未打的猪”、后面打了本回可能会打的那些猪。

      所以可以n*2^n。(一下子就快了10倍呢)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int Lm=18;const double eps=1e-10;
    int T,n,dp[(1<<Lm)+5],lm,tp,list[Lm+5][Lm+5];
    double x[Lm+5],y[Lm+5];
    int solve(int i,int j)
    {
        double a=(y[i]*x[j]/x[i]-y[j])/(x[i]*x[j]-x[j]*x[j]);
        double b=y[i]/x[i]-a*x[i];
        if(a>=-eps)return 0;
        int ret=0;
        for(int u=1;u<=n;u++)
            if(fabs(x[u]*x[u]*a+x[u]*b-y[u])<=eps)ret|=(1<<(u-1));
        return ret;
    }
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            list[i][i]=(1<<(i-1));
            for(int j=i+1;j<=n;j++)
                list[i][j]=solve(i,j);
        }
        memset(dp,1,sizeof dp);dp[0]=0;
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&tp);lm=(1<<n);
            for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
            init();
            for(int s=0;s<lm;s++)
            {
                int i;
                for(i=1;i<=n&&(s&(1<<(i-1)));i++);
                for(int j=i;j<=n;j++)dp[s|list[i][j]]=min(dp[s|list[i][j]],dp[s]+1);
            }
            printf("%d
    ",dp[lm-1]);
        }
        return 0;
    }
    View Code

    但还不够优秀。翻翻洛谷提交记录,看到“文文殿下”惊人的时间,就去学习。

      其实就是用bfs来写。加上第2版的那个优化。

      这样可以得到答案及时退出而不用担心答案不最优(因为是bfs)。

      这大约是利用了  每次转移都只会+1  的性质。能用bfs的话还是它最快呢。(0ms)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int Lm=18,INF=25;const double eps=1e-10;
    int T,n,lm,tp,list[Lm+5][Lm+5];
    int h,t,q[(1<<Lm)+5],dfn[(1<<Lm)+5];
    double x[Lm+5],y[Lm+5];
    bool vis[(1<<Lm)+5];
    int solve(int i,int j)
    {
        double a=(y[i]*x[j]/x[i]-y[j])/(x[i]*x[j]-x[j]*x[j]);
        double b=(y[i]/x[i]-a*x[i]);
        if(a>=-eps)return 0;
        int ret=0;
        for(int u=1;u<=n;u++)
            if(fabs(x[u]*x[u]*a+x[u]*b-y[u])<=eps)ret|=(1<<(u-1));
        return ret;
    }
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            list[i][i]=(1<<(i-1));
            for(int j=i+1;j<=n;j++)
                list[i][j]=solve(i,j);
        }
        memset(vis,0,sizeof vis);
    }
    void bfs()
    {
        h=1;t=0;q[++t]=0;dfn[t]=0;//t=0,多组数据! 
        while(h<=t)
        {
            int s=q[h],d=dfn[h++],i;
            for(i=1;i<=n&&(s&(1<<(i-1)));i++);
            for(int j=i;j<=n;j++)
            {
                int x=(s|list[i][j]);if(vis[x])continue;//
                vis[x]=1;q[++t]=x;dfn[t]=d+1;
                if(x==lm-1){printf("%d
    ",dfn[t]);return;}
            }
        }
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&tp);lm=(1<<n);
            for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
            init();
            bfs();
        }
        return 0;
    }
    View Code
  • 相关阅读:
    使用javap分析Java的字符串操作
    使用javap深入理解Java整型常量和整型变量的区别
    分享一个WebGL开发的网站-用JavaScript + WebGL开发3D模型
    Java动态代理之InvocationHandler最简单的入门教程
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
  • 原文地址:https://www.cnblogs.com/Narh/p/9216078.html
Copyright © 2011-2022 走看看