NOIP2016Day2T3憤怒的小鳥(狀壓dp) O(2^n*n^2)再優化


  看這范圍都知道是狀壓吧。。。

  題目大意就不說了嘿嘿嘿

  網上流傳的寫法復雜度大都是O(2^n*n^2),這個復雜度雖然官方數據可以過,但是在洛谷上會TLE【百度搜出來前幾個博客的代碼交上去都TLE了】,於是造成了洛谷上這題提交5k3只AC了250人,AC率只有4.7%。。。【這么卡常真的喪病,還是因為老爺機?

  所以我就寫寫怎么優化吧OWO

  首先還是先求出兩兩豬的解析式和能穿過多少只豬,記得如果兩個豬的橫坐標相同就要continue,a和b的公式隨手推一推。然后枚舉狀態數,再枚舉哪兩只豬被射,記得處理只射一只豬的情況。

  f[i|num[j][k]]=min(f[i|num[j][k]],f[i]+1);【num[j][k]為一只穿過第j只豬和第k只豬的鳥發射后的狀態

  這就是(2^n*n^2)的寫法,接下來優化。

  對於我們枚舉的每一個狀態i,我們找到它正數第一只沒射掉的豬進行轉移后break。

  因為如果我們轉移了第一只后面的沒射的豬,到時候還要回頭來將第一只豬射掉,所以后面的沒射的豬的轉移其實是多余的,射完第一只豬后接着往后射就可以了。

  【當然只用正數第二只豬或者第三只豬或第四只或第五只轉移都是可以的,一個道理。。。顯然第一只最好寫吧233

  優化之后就可以過洛谷的這題辣。

代碼如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,t,num[20][20],f[1048576];
double x[20],y[20];
bool same(double x,double y){return fabs(x-y)<1e-6;}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)num[i][j]=0;
        for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
        {
            if(same(x[i],x[j]))continue;
            double a=(y[j]/x[j]-y[i]/x[i])/(x[j]-x[i]);
            if(a>=0)continue;
            double b=y[i]/x[i]-a*x[i];
            for(int k=1;k<=n;k++)
            if(same(a*x[k]+b,y[k]/x[k]))num[i][j]|=(1<<(k-1));
        }
        for(int i=0;i<=(1<<n)-1;i++)f[i]=233333333;f[0]=0;
        for(int i=0;i<=(1<<n)-1;i++)
        for(int j=1;j<=n;j++)
        if(!(i&(1<<(j-1))))
        {
            for(int k=j;k<=n;k++)
            {
                if(j==k)f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+1);
                if(same(x[j],x[k]))continue;
                f[i|num[j][k]]=min(f[i|num[j][k]],f[i]+1);
               }
               break;
       }
        printf("%d\n",f[(1<<n)-1]);
    }
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM