题目:给定一个n求1到n之间的2018数,2018数数字中含有 2018即为2018数例如,210198,32108;10218不是2018数,
数据范围1e9;
解法:数位dp,数位dp理解,记忆话加递归,将每一位的数字状态保存下来,以后用到时直接调用结果
代码中写了我对此题的理解 ,可能对大家有点帮助,数位dp入门参看:https://blog.csdn.net/qq_37632935/article/details/81315404#commentBox
#include<iostream>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<stdio.h>
#include<string.h>
using namespace std;
const int n = 11;
int dp[n][5][2];
int dat[5] = { 0,2,0,1,8 };
int num[n+1];
int dfs(int pos, int pn, int lim){
if (pos == 0)return pn == 5 ? 1 : 0;//从高位向低位走到最后,5代表2018走完即前面出现了2018
if (dp[pos][pn][lim]!=-1 && !lim)return dp[pos][pn][lim];//dp[pos][pn][lim]走过,并且当前是没有被限制,即可以随便取.记忆话
int top = lim ? num[pos] : 9;//当前是被限制的则取当前位的值,否则去9,例子3567,当第一位去2时,第二位可以随便取,因为最大
//为2999<3567
int tn = 5;
int ans = 0;
for (int i = 0; i <= top; i++) {
tn = 5;
if(pn<5){//大于等于5即前面的位数已经出现2018的组合,后面的可以全部加起来了
if (dat[pn] == i)tn = pn + 1;//当前位是预期结果,预期结果为向后移一位如,2018当前是2,那么,下次判断就应该判1了
else tn = pn;
}
ans += dfs(pos - 1, tn, lim && (i == top));//当前位只有在前一位也受限制时才受限制。
}
if (!lim)dp[pos][pn][lim] = ans;
return ans;
}
int main() {
memset(dp, -1, sizeof(dp));
int m;
while (cin >> m) {
int k = 0;
while (m) {
num[++k] = m % 10;
m = m / 10;
}
cout << dfs(k, 1, 1);//k位数,从第k位(最高位)开始,第一个1代表dat的2,去判断是否是2,第二个1为当前是否有限制。
}
}