動態規劃之數位動規
阿新 • • 發佈:2018-12-11
#include<bits/stdc++.h> using namespace std; /* 題意:求給出的A和B之間沒有4和連續的37的數字的個數 1<=A<=B<=2000000000 */ int f[11][11]={0}; int solve(int x)//從小到大考慮 { int a[11]={0};//分解x while(x)a[++a[0]]=x%10,x/=10; int ret=0; for(int i=1;i<a[0];i++)//列舉位數,不足a[0]位 for(int j=1;j<=9;j++)ret+=f[i][j];//列舉最高位數字為j for(int i=1;i<a[a[0]];i++)ret+=f[a[0]][i];//最高位不足a[a[0]] if(a[a[0]]==4)return ret; for(int i=a[0]-1;i>=1;i--)//從次高位往下 { //列舉第i位數字為j,依題意和第i+1位比較 for(int j=0;j<a[i]+(i==1);j++)if(j!=4&&!(a[i+1]==3&&j==7))ret+=f[i][j]; //最大的數字已出現不合法情況則直接返回 if(a[i]==4||(a[i+1]==3&&a[i]==7))return ret; } return ret; } int main() { //f[i][j]表示i位數字,首位數字為j的情況總數 for(int i=0;i<=9;i++)if(i!=4)f[1][i]=1;//初始化1位的情況 for(int i=2;i<=10;i++)//列舉位數 for(int j=0;j<=9;j++)//列舉首位//注意在初始化f的過程中前導0是允許的 for(int k=0;k<=9;k++)//列舉次位 { if(j!=4&&k!=4&&!(j==3&&k==7))f[i][j]+=f[i-1][k]; } int x,y; scanf("%d%d",&x,&y); printf("%d\n",solve(y)-solve(x-1));//注意邊界值x也要考慮 return 0; }
#include<bits/stdc++.h> using namespace std; /* 題意:求區間[X,Y]內恰好等於K個互不相等的B的整數次冪之和的數的個數 資料範圍:1<=X<=Y<=2^31-1 1<=K<=20 2<=B<=10 思路: 把詢問的數轉換成B進位制xb,則要求的數為每個B進位制數位上有K個1且小於xb的數 設f(i,j,k)表示第i位,首位為j,已經用了k個1的數的個數 */ int X,Y,K,B,a[35]={0},f[35][2][25]={0}; int cal(int x) { int ret=0;a[0]=0; while(x)a[++a[0]]=x%B,x/=B;//把詢問的數轉換成B進位制 for(int i=1;i<=a[0]-1;i++)ret+=f[i][1][K];//列舉位數小於a的數 if(a[a[0]]>1)return ret+f[a[0]][1][K];//最高位已經大於1了,就直接返回了 int sum=1;//從高位推到低位時,已經用了的1的個數 for(int i=a[0]-1;i>=1;i--) { if(a[i]==1) { ret+=f[i][0][K-sum];//當前位為1則可以加上小於當前位的所有情況 sum++;//把當前位為1的情況加上 } //超過1則包含所有情況 else if(a[i])return ret+f[i][0][K-sum]+f[i][1][K-sum]; if(i==1)ret+=f[i][0][K-sum];//??? } return ret; } int main() { scanf("%d%d%d%d",&X,&Y,&K,&B); //初始化 f[1][0][0]=1;//1位,首位為0、用了0個1的數有一個 f[1][1][1]=1;//1位,首位為1、用了1個1的數有一個 for(int i=2;i<=30;i++)//列舉B進位制數,每個數位全為1或0的數的位數 for(int j=0;j<=1;j++)//列舉首位為0或1 for(int k=0;k<=K;k++)//列舉已經用了的1的個數 for(int t=0;t<=1;t++)//列舉上一個狀態已經用了的1的個數 if(k-j>=0)f[i][j][k]+=f[i-1][t][k-j]; cout<<cal(Y)-cal(X-1)<<endl; return 0; }