二進制上的數位dpPOJ 3252
阿新 • • 發佈:2019-03-01
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> 2RNGRNGRNG#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,反之返回011 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 }
關於結果的存儲,不同的想法具體的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