推薦以下一篇博客: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 }
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 }
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 }
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 }
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 }
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 }
7.(HDOJ3709)http://acm.hdu.edu.cn/showproblem.php?pid=3709
題意:求區間[L, R]內平衡數的個數 平衡數的定義是指,以某位作為支點,此位的左面(數字 * 距離)之和 與右邊相等,距離是指某位到支點的距離;
分析:首先需要明確一個數如果是平衡數,那么它的支點一定是確定。所以需要枚舉支點是哪個點。要注意在數位dp的dfs中,由於 0 對於每個位置都會被統計到,最后要再減去重復的。

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][20][1800]; 8 ll dfs(int pos,int bal,int sum,bool limit) 9 { 10 if ( pos==-1 ) return sum==0; 11 if ( !limit && dp[pos][bal][sum]!=-1 ) return dp[pos][bal][sum]; 12 int up=limit?a[pos]:9; 13 ll tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if ( sum+(pos-bal)*i<0 ) continue; 17 tmp+=dfs(pos-1,bal,sum+(pos-bal)*i,limit&&i==a[pos]); 18 } 19 if ( !limit ) dp[pos][bal][sum]=tmp; 20 return tmp; 21 } 22 23 ll solve(ll x) 24 { 25 if ( x==-1 ) return 0; 26 int pos=0; 27 while ( x ) 28 { 29 a[pos++]=x%10; 30 x/=10; 31 } 32 ll ans=0; 33 for ( int i=0;i<pos;i++ ) ans+=dfs(pos-1,i,0,true); 34 return ans-pos+1; 35 } 36 37 int main() 38 { 39 ll l,r; 40 int T; 41 memset(dp,-1,sizeof(dp)); 42 scanf("%d",&T); 43 while ( T-- ) 44 { 45 scanf("%I64d%I64d",&l,&r); 46 printf("%I64d\n",solve(r)-solve(l-1)); 47 } 48 return 0; 49 }
8.(HDOJ4507)http://acm.hdu.edu.cn/showproblem.php?pid=4507
分析:dp[i][j][k]第二維表示%7后的余數,第三維表示所有位數的和%7后的值。因為要求的是所有滿足條件的數的平方和,我們考慮對一個數按平方和規則進行拆分。
結構體數組dp中記錄三個值,cnt代表滿足條件的數的個數,sum表示滿足條件的數的求和,sum2表示滿足條件的數的平方和.
以下轉移式為不帶取模的轉移方程,假設第i位是當前這位,pos為當前所處的位置,p[pos]代表10^pos次,當前的狀態為ans,子狀態為tmp(方法:先考慮單個式子再進行累加)
ans.cnt+=tmp.cnt
ans.sum+=(tmp.sum+tmp.cnt*(i*p[pos]))
ans.sum2+=(tmp.cnt*((i*p[pos])^2)+2*(i*p[i])*tmp.sum+tmp.sum2)

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const ll mod=1e9+7; 7 int a[20]; 8 ll p[20]; 9 struct node{ 10 ll cnt,sum,sum2; 11 node(ll _cnt=0,ll _sum=0,ll _sum2=0):cnt(_cnt),sum(_sum),sum2(_sum2) {} 12 }dp[20][10][10]; 13 14 node dfs(int pos,int rem,int snum,bool limit) 15 { 16 if ( pos==-1 ) 17 { 18 node ss(0,0,0); 19 if ( rem!=0 && snum!=0 ) ss.cnt=1; 20 return ss; 21 } 22 if ( !limit && dp[pos][rem][snum].sum2!=-1 ) return dp[pos][rem][snum]; 23 int up=limit?a[pos]:9; 24 node ans(0,0,0); 25 for ( int i=0;i<=up;i++ ) 26 { 27 if ( i==7 ) continue; 28 node tmp=dfs(pos-1,(rem*10+i)%7,(snum+i)%7,limit&&i==a[pos]); 29 ans.cnt+=tmp.cnt; 30 ans.cnt%=mod; 31 ans.sum+=(tmp.sum+i*p[pos]%mod*tmp.cnt%mod)%mod; 32 ans.sum%=mod; 33 ans.sum2+=(tmp.sum2+2*p[pos]*i%mod*tmp.sum%mod)%mod; 34 ans.sum2%=mod; 35 ans.sum2+=(tmp.cnt*p[pos]%mod*p[pos]%mod*i*i%mod); 36 ans.sum2%=mod; 37 } 38 if ( !limit ) dp[pos][rem][snum]=ans; 39 return ans; 40 } 41 42 ll solve(ll x) 43 { 44 int pos=0; 45 while ( x ) 46 { 47 a[pos++]=x%10; 48 x/=10; 49 } 50 node tmp=dfs(pos-1,0,0,true); 51 return tmp.sum2; 52 } 53 54 int main() 55 { 56 ll l,r; 57 int T; 58 memset(dp,-1,sizeof(dp)); 59 p[0]=1; 60 for ( int i=1;i<=18;i++ ) p[i]=(p[i-1]*10)%mod; 61 scanf("%d",&T); 62 while ( T-- ) 63 { 64 scanf("%I64d%I64d",&l,&r); 65 printf("%I64d\n",((solve(r)-solve(l-1))%mod+mod)%mod); 66 } 67 return 0; 68 }
9.(HDOJ3886)http://acm.hdu.edu.cn/showproblem.php?pid=3886
題意:給出一個字符,只含'/','-' ,'\' ,表示着一個數上的各位數字按相應字符上升,不變或下降,問【a,b】區間內這樣的數有多少個?
分析;dp[i][j][k]第二維表示前一個值是什么,第三維表示當前處於波動字符串的第幾個位置
要注意幾點:
a、為了確保每個數只被計算一次,當能進入一個新的起伏時,盡量先進入,如果不能再判斷是否符合之前的起伏。
b、前導零不應該被算入起伏中,起伏只能在沒有前導零的數中匹配。
c、n個運算符至少要有n+1個數。
d、注意開始的狀態,第一個數以及第一個運算符。
e、注意大數減一的計算。
f、注意減法的取模。

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn=105; 7 const int mod=1e8; 8 char s1[maxn],s2[maxn],op[maxn]; 9 int a[maxn]; 10 int dp[maxn][12][maxn],len; 11 12 bool check ( int x , int y , char c ) 13 { 14 if ( c == '/' && x < y ) return true ; 15 if ( c == '-' && x == y ) return true ; 16 if ( c == '\\' && x > y ) return true ; 17 return false ; 18 } 19 20 ll dfs(int pos,int pre,int p,bool lead,bool limit) 21 { 22 if ( pos==-1 ) return p==len; 23 if ( !limit && !lead && dp[pos][pre][p]!=-1 ) return dp[pos][pre][p]; 24 int up=limit?a[pos]:9; 25 ll tmp=0; 26 for ( int i=0;i<=up;i++ ) 27 { 28 29 if ( lead ) tmp=(tmp+dfs(pos-1,i,p,lead&&i==0,limit&&i==a[pos]))%mod; 30 else if ( p<len && check(pre,i,op[p+1]) ) tmp=(tmp+dfs(pos-1,i,p+1,lead,limit&&i==a[pos]))%mod; 31 else if ( p>0 && check(pre,i,op[p]) ) tmp=(tmp+dfs(pos-1,i,p,lead,limit&&i==a[pos]))%mod; 32 } 33 if ( !limit&&!lead ) dp[pos][pre][p]=tmp%mod; 34 return tmp; 35 } 36 37 ll solve(char *x,int f) 38 { 39 int i=0,pos=0; 40 int len_=strlen(x); 41 while ( x[i]=='0' ) i++; 42 if ( x[i]=='\0' ) return 0; 43 for( int j=len_-1;j>=i;j-- ) a[pos++]=x[j]-'0'; 44 if( f ) 45 { 46 a[0]--; 47 for( int j=0;j<pos;j++ ) 48 { 49 if(a[j]<0) 50 { 51 a[j]+=10; 52 a[j+1]--; 53 } 54 } 55 } 56 pos--; 57 if( a[pos]==0 ) pos--; 58 return dfs(pos,0,0,true,true)%mod; 59 } 60 61 int main() 62 { 63 int l,r; 64 while ( scanf("%s",op+1)!=EOF ) 65 { 66 memset(dp,-1,sizeof(dp)); 67 len=strlen(op+1); 68 scanf("%s%s",s1,s2); 69 ll r=solve(s2,0); 70 ll l=solve(s1,1); 71 printf("%08lld\n",(r-l+mod)%mod); 72 } 73 return 0; 74 }
10.(HDOJ4352)http://acm.hdu.edu.cn/showproblem.php?pid=4352
題意:給定一個區間,讓你求這個區間中數位從左到右滿足LIS恰好為m的數的個數
分析:dp[i][j][k],第二維表示當前的狀態(即LIS),第三維表示所求的LIS的長度
第二維中要用到LIS的nlogn的算法,具體見https://blog.csdn.net/shuangde800/article/details/7474903。 大致的含義就是j的二進制表示下有多少個位置為1代表LIS的長度為多少,從小到大出現的第i個1的位置j代表長度為i的LIS結尾為j。每次更新時,不斷更新j(大致思想就是nlogn的LIS做法),注意記憶化返回的是要求的LIS長度的狀態,而不是當前狀態的LIS長度的狀態

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[25],m; 7 ll dp[25][1030][12]; 8 9 int getnews(int x,int s) 10 { 11 for( int i=x;i<=9;i++ ) 12 if(s&(1<<i)) return (s^(1<<i))|(1<<x); 13 return s|(1<<x); 14 } 15 16 int count(int x) 17 { 18 int cnt=0; 19 while ( x ) 20 { 21 if ( x&1 ) cnt++; 22 x/=2; 23 } 24 return cnt; 25 } 26 27 ll dfs(int pos,int sta,bool lead,bool limit) 28 { 29 if ( pos==-1 ) return count(sta)==m; 30 if ( !limit && !lead && dp[pos][sta][m]!=-1 ) return dp[pos][sta][m]; 31 int up=limit?a[pos]:9; 32 ll tmp=0; 33 for ( int i=0;i<=up;i++ ) 34 { 35 tmp+=dfs(pos-1,(lead&&i==0)?0:getnews(i,sta),lead&&i==0,limit&&i==a[pos]); 36 } 37 if ( !limit&&!lead ) dp[pos][sta][m]=tmp; 38 return tmp; 39 } 40 41 ll solve(ll x) 42 { 43 int pos=0; 44 while ( x ) 45 { 46 a[pos++]=x%10; 47 x/=10; 48 } 49 return dfs(pos-1,0,true,true); 50 } 51 52 int main() 53 { 54 ll l,r; 55 int T,h; 56 memset(dp,-1,sizeof(dp)); 57 scanf("%d",&T); 58 for ( h=1;h<=T;h++ ) 59 { 60 scanf("%lld%lld%d",&l,&r,&m); 61 printf("Case #%d: %lld\n",h,solve(r)-solve(l-1)); 62 } 63 return 0; 64 }