对于一个正整数x,如果x的每一位都不大于它右边一位上的数字,那么就称x是递增数,例如:112,4557,18899,111。
类似的,如果x的每一位都不小于它右边一位上的数字,那么就称x是递减数,例如:986,6331,77311,111。
递增数和递减数统称单调数。(111既是递增数,也是递减数,所以111肯定是单调数)
对于一个正整数x,如果x的每一位都不大于它右边一位上的数字,那么就称x是递增数,例如:112,4557,18899,111。
类似的,如果x的每一位都不小于它右边一位上的数字,那么就称x是递减数,例如:986,6331,77311,111。
递增数和递减数统称单调数。(111既是递增数,也是递减数,所以111肯定是单调数)
有多组输入。
每组输入一个数n。(n<=100)
对于每组输入数据中的n,输出小于10^n的单调数个数。
6
10
12951
277032
数位dp,想了好几种状态表示方案最后才成功,无语啊。。。
用dp[i][j]表示长度为i位,各位都不超过j时的单调数数量;
那么当i=1时,显然dp[i][j]=1 (j=1,2,...9)
当i=2时,以dp[2][3]为例,可能情况如下:
13,31, 23,32, 30, 33
共6种,下面分别讲解这六种情况:
13,31这两个数可以看作是分别在1这个数字前后添上3得到的,23,32同理;而03这种情况实际就是3,不符合,那么剩下30;而在3的前后添3都是33;
由以上可得规律:当i=2时,dp[2][j]=2+∑(dp[i-1][k]*2) (0<k<j)
而当i>2时,情况又有不同,以dp[3][3]为例:
通过之前的两步,已经可以得到得到i,j为(2,1),(2,2),(2,3)时的dp值,其具体情况如下:
(i,j) | 可能情况 | dp[i][j] |
(2,1) | 10,11 | 2 |
(2,2) | 12,21, 20, 22 | 4 |
(2,3) | 13,31, 23,32, 30, 33 | 6 |
可能情况 | ||
310,311 | ||
123,321, 320, 322 | ||
133,331, 233,332, 330, 333 |
但是!要注意,现在并没有找到所有情况,因为对于11,22这样的数,3仍可以随意加到其前后构成311,113,322,223,而33只能构成333.所以对于前j-1行,每行都要再多一种情况。
再检查一下是否所有可能情况已经找全了?
坑爹的我最后才发现竟然把300给忘了!
那么,当i>2时,最终得到的状态转移方程即为:dp[i][j]=∑(dp[i-1][k]+1) (1<=k<=j)
至此,规律已经找好了,接下来就是打表,计算
对于给定的n,要求的是小于10^n的单调数个数,即i可以等于1、2、3...n,j可以等于0、1、2...9;
那么,ans=∑∑dp[i][j] (i=1 to n)(j=0 to 9);
呼~解释完了,最后放一下AC代码:
(因为当时时间紧急,很多地方都写得很乱套,很多地方其实可以优化,不过总算是在比赛结束前两分钟的紧张时刻ac了,还是比较hp的,放上来仅作参考吧
#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long dp[105][15]; int main() { long long n; for(long long i=0;i<=9;++i) { dp[1][i]=1; } for(long long i=2;i<=2;++i) { dp[i][0]=0; for(long long j=1;j<=9;++j) { for(long long k=1;k<j;++k) { dp[i][j]+=(dp[i-1][k]*2); } dp[i][j]+=2; } } dp[1][0]=0; for(long long i=3;i<=100;++i) { dp[i][0]=0; for(long long j=1;j<=9;++j) { for(long long k=1;k<=j;++k) { dp[i][j]+=(dp[i-1][k]+1); } --dp[i][j]; dp[i][j]+=1; } } while(cin>>n) { long long ans=0; for(int i=1;i<=n;++i) { for(long long j=0;j<=9;++j) { ans+=dp[i][j]; } } cout<<ans<<endl; } return 0; }
原文:http://blog.csdn.net/harrypoirot/article/details/23611567