1. 程式人生 > >專題訓練之數位DP

專題訓練之數位DP

https lld cst class 範圍 不出 mit print 添加

推薦以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392

1.(HDOJ2089)http://acm.hdu.edu.cn/showproblem.php?pid=2089

分析:裸模板題

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][2];
 8 int dfs(int pos,int
pre,int sta,bool limit) 9 { 10 if ( pos==-1 ) return 1; 11 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta]; 12 int up=limit?a[pos]:9; 13 int tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if ( pre==6 && i==2 ) continue; 17 if ( i==4 ) continue
; 18 tmp+=dfs(pos-1,i,i==6,limit&&i==a[pos]); 19 } 20 if ( !limit ) dp[pos][sta]=tmp; 21 return tmp; 22 } 23 24 int solve(int x) 25 { 26 int pos=0; 27 while ( x ) 28 { 29 a[pos++]=x%10; 30 x/=10; 31 } 32 return dfs(pos-1,-1,0,true); 33 }
34 35 int main() 36 { 37 int l,r; 38 while ( scanf("%d%d",&l,&r)!=EOF && (l+r) ) 39 { 40 memset(dp,-1,sizeof(dp)); 41 printf("%d\n",solve(r)-solve(l-1)); 42 } 43 return 0; 44 }
HDOJ2089

2.(HDOJ3555)http://acm.hdu.edu.cn/showproblem.php?pid=3555

題意:求區間內不出現49的數的個數

分析:裸模板題

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 ll dp[20][2];
 8 ll dfs(int pos,int pre,int sta,bool limit)
 9 {
10     if ( pos==-1 ) return 1;
11     if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:9;
13     ll tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if ( pre==4 && i==9 ) continue;
17         tmp+=dfs(pos-1,i,i==4,limit&&i==a[pos]);
18     }    
19     if ( !limit ) dp[pos][sta]=tmp;
20     return tmp;
21 }
22 
23 ll solve(ll x)
24 {
25     int pos=0;
26     while ( x )
27     {
28         a[pos++]=x%10;
29         x/=10;
30     }
31     return dfs(pos-1,-1,0,true);
32 }
33 
34 int main()
35 {
36     ll l,r,T;
37     memset(dp,-1,sizeof(dp));
38     scanf("%lld",&T);
39     while ( T-- )
40     {
41         scanf("%lld",&r);
42         printf("%lld\n",r-solve(r)+1);
43     }
44     return 0;
45 }
HDOJ3555

3.(HDOJ4734)http://acm.hdu.edu.cn/showproblem.php?pid=4734

題意:題目給了個f(x)的定義:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十進制數位,然後給出a,b求區間[0,b]內滿足f(i)<=f(a)的i的個數。

分析:采用相減的思想,dp[i][j],第一維表示處於數字的第幾位(即pos),第二維表示是枚舉到當前pos位,後面位數最多能湊的權值和為j(起始值為f(a)),最後當j>=0是滿足條件的數。具體解釋見上面的博客

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=10010;
 7 int a[12];
 8 int dp[12][maxn];
 9 int sum;
10 int pow_[maxn];
11 
12 int dfs(int pos,int sta,bool limit)
13 {
14     if ( pos==-1 ) return 1;
15     if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
16     int up=limit?a[pos]:9;
17     int tmp=0;
18     for ( int i=0;i<=up;i++ )
19     {
20         int x=pow_[pos]*i;
21         if ( sta-x<0 ) continue;
22         tmp+=dfs(pos-1,sta-x,limit&&i==a[pos]);
23     }
24     if ( !limit ) dp[pos][sta]=tmp;
25     return tmp;
26 }
27 
28 int solve(int x)
29 {
30     int pos=0;
31     while ( x )
32     {
33         a[pos++]=x%10;
34         x/=10;    
35     }
36     return dfs(pos-1,sum,true);
37 }
38 
39 int main()
40 {
41     int l,r,T,i,j,k,h,A,B,x,y,z,cnt;
42     pow_[0]=1;
43     for ( i=1;i<=8;i++ ) pow_[i]=pow_[i-1]*2;
44     scanf("%d",&T);
45     memset(dp,-1,sizeof(dp));
46     for ( h=1;h<=T;h++ )
47     {
48         scanf("%d%d",&A,&B);
49         sum=0;
50         cnt=0;
51         x=A;
52         while ( x )
53         {
54             y=x%10;
55             sum+=y*pow_[cnt++];
56             x/=10;
57         }
58         printf("Case #%d: %d\n",h,solve(B));
59     }
60     return 0;
61 }
HDOJ4734

4.(POJ3252)http://poj.org/problem?id=3252

題意:求一個範圍內滿足,二進制下0的位數>=1的位數的個數

分析:將原先的十進制一位轉化為二進制一位.此題要在dfs中添加bool型的lead表示前導0,因為這題需要拿0的個數和1的個數比較,所以需要考慮前導0.同時dp數組的第二維記錄的是0的個數-1的個數。因為可能為負,所以初始值不為0,而記一個較大的數(我記的是32,只要能使得過程不為負即可)

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[50];
 7 ll dp[50][66];
 8 ll dfs(int pos,int sta,bool lead,bool limit)
 9 {
10     if ( pos==-1 ) return sta>=32;
11     if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:1;
13     ll tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if(lead && i==0) tmp+=dfs(pos-1,sta,lead,limit && i==a[pos]);
17         else tmp+=dfs(pos-1,sta+(i==0?1:-1),lead && i==0,limit && i==a[pos]);  
18     }    
19     if ( !limit&&!lead ) dp[pos][sta]=tmp;
20     return tmp;
21 }
22 
23 ll solve(ll x)
24 {
25     int pos=0;
26     while ( x )
27     {
28         a[pos++]=x%2;
29         x/=2;
30     }
31     return dfs(pos-1,32,true,true);
32 }
33 
34 int main()
35 {
36     ll l,r;
37     memset(dp,-1,sizeof(dp));
38     while ( scanf("%lld%lld",&l,&r)!=EOF )
39     {
40         printf("%lld\n",solve(r)-solve(l-1));
41     }
42     return 0;
43 }
POJ3252

5.(HDOJ5179)http://acm.hdu.edu.cn/showproblem.php?pid=5179

題意:給定一個數A,a數組從0開始對應著數A從左到右的每一尾,現在要求數A右邊的數都不比左邊的數大,同時要求在左邊的數去模右邊的數都為0

分析:dp數組的第二維記錄前一位數是多少即可。轉移時要轉移到比前一位數小的數同時要被前一位數取模為0的數

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][12];
 8 int dfs(int pos,int sta,bool lead,bool limit)
 9 {
10     if ( pos==-1 ) return 1;
11     if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:9;
13     int tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if ( !lead&&i==0 ) continue;
17         if ( lead )
18         {
19             tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
20             continue;
21         }
22         if ( i>sta&&sta!=-1 ) break;
23         if ( sta%i!=0 && sta!=-1 ) continue;
24         tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
25     }    
26     if ( !limit&&!lead ) dp[pos][sta]=tmp;
27     return tmp;
28 }
29 
30 int solve(int x)
31 {
32     int pos=0;
33     while ( x )
34     {
35         a[pos++]=x%10;
36         x/=10;
37     }
38     return dfs(pos-1,-1,true,true);
39 }
40 
41 int main()
42 {
43     int l,r,T;
44     scanf("%d",&T);
45     memset(dp,-1,sizeof(dp));
46     while ( T-- )
47     {
48         scanf("%d%d",&l,&r);
49         printf("%d\n",solve(r)-solve(l-1));
50     }
51     return 0;
52 }
HDOJ5179

6.(HDOJ3652)http://acm.hdu.edu.cn/showproblem.php?pid=3652

題意:給定一個範圍,求該範圍內不含13同時不是13倍數的數的個數

分析:設置三維數組dp[i][j][k],第一維表示位置,第二維表示13的余數,

第三維有三個值,0代表此前還未出現過13同時前一位不為1,1代表此前還未出現過13同時前1位位1,2代表此前已經出現過了13

最後判斷時,只有當第二維為0,第三維為2時才加入計數

註意第三維轉移時到底是多少

技術分享圖片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][15][3];
 8 int dfs(int pos,int rem,int sta,bool limit)
 9 {
10     if ( pos==-1 ) return rem==0&&sta==2;
11     if ( !limit && dp[pos][rem][sta]!=-1 ) return dp[pos][rem][sta];
12     int up=limit?a[pos]:9;
13     int tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         int x=sta;
17         if ( sta==0 && i==1 ) x=1;
18         if ( sta==1 && i!=1 ) x=0;
19         if ( sta==1 && i==3 ) x=2;
20         tmp+=dfs(pos-1,(rem*10+i)%13,x,limit&&i==a[pos]);
21     }    
22     if ( !limit ) dp[pos][rem][sta]=tmp;
23     return tmp;
24 }
25 
26 int solve(int x)
27 {
28     int pos=0;
29     while ( x )
30     {
31         a[pos++]=x%10;
32         x/=10;
33     }
34     return dfs(pos-1,0,0,true);
35 }
36 
37 int main()
38 {
39     int l,r;
40     memset(dp,-1,sizeof(dp));
41     while ( scanf("%d",&r)!=EOF )
42     {
43         printf("%d\n",solve(r));
44     }
45     return 0;
46 }
HDOJ3652

專題訓練之數位DP