1. 程式人生 > >二進制上的數位dpPOJ 3252

二進制上的數位dpPOJ 3252

col 計算 lse 計數 相同 ble size 下界 void

Round number POJ - 3252

題目大意:一個"round number" 數的定義是,將它轉化成2進制後,0的個數大於等於1的個數,要求的就是在[s,f]範圍內"round number"的個數

和之前的數位dp不同的是,這題是對二進制的數位進行dp,所以就存在著一個限制,前面有沒有存在1,如果前面沒有1.那麽像00001,就是1,前面的0是不計數的,所以重點就在於考慮這個

dfs時就得分前面有沒有1,如果沒有那麽0就不計數,反之我們記錄0和1的差值,取0加1,取1減1,不過需要註意的是,差值可能是負數,所以要轉正數,詳情見代碼

技術分享圖片
 1 #include<cstdio>
 2
#include<cstring> 3 #define ll long long 4 const int N=50; 5 int up[N]; 6 ll n,s,f,dp[N][2*N][2];//dp[i][j][k]代表當前為第i位,前面0和1的差值為j-N,前面有沒有1的結果 7 //例如dp[4][50][1]就代表第4位,在第4位前面0和1差值為0,並且前面有1的結果數 8 ll dfs(int p,int d,bool is1,bool isu)//p 位數,d 0和1的差值,is1 該位前面有沒有1 isu有沒有上限 9 { 10 if(!p)//邊緣狀態,0大於等於1(d>=0)返回1,反之返回0
11 return d>=0; 12 if(-d>p)//小剪枝,如果剩下位數全是0加起來都沒有1多,返回0 13 return 0; 14 if(!isu&&dp[p][d+N][is1]!=-1) 15 return dp[p][d+N][is1]; 16 ll ans=0; 17 for(int i=0;i<=(isu ? up[p] : 1);i++) 18 { 19 if(is1)//當前面有1正常記錄0的差值 20 ans+=dfs(p-1
,(i ? d-1 : d+1),is1||i,isu&&i==up[p]); 21 else//當前面沒有1,0不計,1記-1 22 ans+=dfs(p-1,-i,is1||i,isu&&i==up[p]); 23 } 24 if(!isu) 25 dp[p][d+N][is1]=ans; 26 return ans; 27 } 28 ll solve(ll x) 29 { 30 int num=0; 31 while(x) 32 { 33 up[++num]=x&1; 34 x>>=1; 35 } 36 return dfs(num,0,0,1); 37 } 38 int main() 39 { 40 ll s,f; 41 memset(dp,-1,sizeof(dp)); 42 while(~scanf("%lld%lld",&s,&f)) 43 printf("%lld\n",solve(f)-solve(s-1)); 44 return 0; 45 }
RNGRNGRNG

關於結果的存儲,不同的想法具體的dp還有也不同,有些是dp[i][j][k]是當前是第i位,前面0的個數為j,前面1的個數為k的結果有多少個,不過大體的思路還是相同的

除數位dp外,還有一個組合數學的解法,不過相比大佬們,我的思路有點麻煩,詳情見代碼

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #define ll long long
 4 ll n,m,c[36][36]={1},a[36]={0};//c組合數,a[i]長度為i,首位1在第i位存在RN的個數 
 5 //比加a[1]保存1,a[2]保存10,11,a[3]保存100,101,110,111,中滿足RN的個數
 6 //所以a[1]=0,a[2]=1,a[3]=1 
 7 int up[36]={1};
 8 void init()
 9 {
10     for(int i=1;i<=32;i++)
11     {
12         c[i][0]=1;
13         for(int j=1;j<=i;j++)
14         {
15             if(j<=i/2)
16                 c[i][j]=c[i-1][j-1]+c[i-1][j];
17             else
18                 c[i][j]=c[i][i-j];
19         }
20         for(int j=i-1;j>=(i+1)/2;j--)//首位i是1,剩下i-1位0可以i-1個,
21         //i-2個,假設最少取j個滿足要求,那麽j>=(i+1)/2(0的數目大於等於總數目的一半) 
22             a[i]+=c[i-1][j];
23     }
24 }
25 ll solve(ll x)
26 {
27     int num=0;
28     while(x)
29     {
30         up[++num]=x&1;
31         x>>=1;
32     }
33     ll ans=0;
34     for(int i=1;i<num;i++)
35         ans+=a[i];
36     //假如x轉化成2進制是1101
37     //先算上1,10~11,100~111的裏的答案 
38     if(num>1)//先把1000算上 
39         ans++;
40     int z=0;//記錄第i位前0的個數 
41     //再來計算1000~1101之間的答案,down代表有沒有計算到下界
42     //一開始的下界就是1000                                   
43     for(int i=num-1,down=1;i>=1;i--)
44     {
45         if(up[i])//當存在1時,例如第二位就是1,我們就求的是1000~1100的答案 
46         {
47             if(down)//因為之前已經算過1000了,所以這裏減去
48                 ans--,down=0;
49             for(int j=i-1;z+j+1>=(num+1)/2&&j>=0;j--)//假設第i位是0,前面的0加上
50             //第i位再加上後面取的j個0,總的0的個數要大於等於總個數 
51                 ans+=c[i-1][j];
52             if(i-1+z>=(num+1)/2)//假設後面的i-1全是0,0的個數大於等於總個數 
53                 ans++,down=1;//那麽1100可以作為下界 
54         }
55         else
56             z++;
57     }
58     return ans;
59 }
60 int main()
61 {
62     init();
63     while(~scanf("%lld%lld",&n,&m))
64         printf("%lld\n",solve(m)-solve(n-1));
65     return 0;
66 }
WEWEWE

二進制上的數位dpPOJ 3252