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
  • 相关阅读:
    1438.最小公倍数
    1441.人见人爱A^B
    1083.特殊乘法
    1153.括号匹配
    1089.数字翻转
    1042.coincidence(动态规划求最长公共子序列)
    图的m着色问题pascal程序
    最佳调度问题pascal程序
    试卷批分打表程序
    迷宫问题pascal程序
  • 原文地址:https://www.cnblogs.com/Narh/p/9216078.html
Copyright © 2011-2022 走看看