绝了,又发现我之前数位dp板子是错的。。写了那么久才发现,之前样例太水了。
233.数字1的个数
class Solution {
public:
struct node{
int cnt,sum;
node(){sum=-1;}
node(int cnt,int sum){this->cnt=cnt,this->sum=sum;}
}dp[22];
int a[22];
node num_dp(int pos,int limit){
if(!pos) return (node){1,0};
if(!limit&&dp[pos].sum!=-1) return dp[pos];
int up=limit?a[pos]:9;
node ans={0,0};
for(int i=0;i<=up;++i){
node cur=num_dp(pos-1,limit&&(i==a[pos]));
ans.cnt+=cur.cnt,ans.sum+=cur.sum;
if(i==1) ans.sum+=cur.cnt; //第pos位等于1的时候
}
if(!limit) dp[pos]=ans;
return ans;
}
int flag=0;
int countDigitOne(int n) {
if(!flag){//多次调用同一个对象的这个函数,数位dp才有意义,只调用一次dp白记忆化了
int o=1e7,p=0;
while(o) a[++p]=o%10,o/=10;
num_dp(p,1);
flag=1;
}
int p=0;
while(n) a[++p]=n%10,n/=10;
node ans=num_dp(p,1);
return ans.sum;
}
};
600. 不含连续1的非负整数
class Solution {
public:
int a[34],dp[34][2];
int num_dp(int pos,int pre,int limit){
if(!pos) return 1;
if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre];
int up=limit?a[pos]:1;
int ans=0;
for(int i=0;i<=up;++i)
if(!pre||i!=1) ans+=num_dp(pos-1,(i==1),limit&&(i==a[pos]));
if(!limit) dp[pos][pre]=ans;
return ans;
}
int flag;
void init(){
int o=1e9,p=0;
while(o) a[++p]=o%2,o/=2;
num_dp(p,0,1);
}
int findIntegers(int n) {
if(!flag) {
memset(dp,-1,sizeof dp);
init(),flag=1;
}
int p=0;
while(n) a[++p]=n%2,n/=2;
return num_dp(p,0,1);
}
};
357. 计算各个位数不同的数字个数(处理前导0)
题目
注意前面的位数都是0的话也就是有前导0,前面0其实都不算被使用。因为000123实际就是123.所以这题要处理前导0
class Solution {
public:
int dp[22][(1<<10)+10],a[22];//因为是111111111 共9个1 所以不能(1<<9)+10
int num_dp(int pos,int pre,int lead,int limit){
if(!pos) return 1;
if(!limit&&!lead&&dp[pos][pre]!=-1) return dp[pos][pre];
int up=limit?a[pos]:9,ans=0;
for(int i=0;i<=up;++i){
if(lead&&(!i)) ans+=num_dp(pos-1,0,1,limit&&(i==a[pos]));//前面都是0 该位还是0 这时候0并不算被使用
else{
if(pre&(1<<i)) continue;
ans+=num_dp(pos-1,pre|(1<<i),lead&&(!i),limit&&(i==a[pos]));
}
}
if(!limit&&!lead) dp[pos][pre]=ans;
return ans;
}
int flag;
void init(){
int o=1e8,p=0;
while(o) a[++p]=o%10,o/=10;
num_dp(p,0,1,1);
}
int countNumbersWithUniqueDigits(int n) {
if(!flag){
memset(dp,-1,sizeof dp);
init(),flag=1;
}
n=pow(10,n),--n;//[0,10^n) 开区间,,何必呢
int p=0;
while(n) a[++p]=n%10,n/=10;
return num_dp(p,0,1,1);
}
};
902. 最大为 N 的数字组合(处理前导0)
题目
有序集合中不含0,但00035又是合法的,要处理前导0.
class Solution {
public:
int dp[25],a[25];
int num_dp(int pos,int lead,int limit,vector<int>&d){
if(!pos) return 1;
if(!limit&&!lead&&dp[pos]!=-1) return dp[pos];
int ans=0;
for(int i=0;i<d.size();++i){
if(limit&&a[pos]<d[i]) break;
ans+=num_dp(pos-1,0,limit&&(d[i]==a[pos]),d);
}
if(lead) ans+=num_dp(pos-1,1,limit&&(0==a[pos]),d);//假如不想算0 可以if(head&&pos!=1)
if(!limit&&!lead) dp[pos]=ans;
return ans;
}
int cal(string s){
int ans=0;
for(char c:s) ans=ans*10+(c-'0');
return ans;
}
int atMostNGivenDigitSet(vector<string>& digits, int n) {
memset(dp,-1,sizeof dp);
vector<int> d;
for(int i=0;i<digits.size();++i) d.push_back(cal(digits[i]));
int p=0;
while(n) a[++p]=n%10,n/=10;
return num_dp(p,1,1,d)-1;//把0算上去了
}
};