HDU 2089 不要62
限制:
62 不能连号, 不能出现4
思路:
1. 对于当前位数字是 4 有限制,可以直接处理!
2.
pre_num(前一个数字) 有限制;
if(pre_num == 6) dp当前位不能取 2 状态变化.
else dp数组可以任意取.
所以对于当前位 的 前面位有两种情况,
但是我们是写的DFS,所以先对于后面的状态先更新,所以dp更新的时候对于后一位要记录dp[][0/1] (0/1代表 pre_num(=6 / !=6 ))
int dp[10][2], a[10];
int DFS(int pos, int sta, bool limit){
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
int temp = 0;
for(int i=0;i<=up;i++){
if(i == 4) continue;
if(sta && i == 2) continue;
temp = temp + DFS(pos-1, i==6, limit && (i == up));
}
if(!limit) dp[pos][sta] = temp;
return temp;
}
int solve(int n){
int num = 0;
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, true);
}
int main(){
int n, m;
memset(dp, -1, sizeof(dp));
while(scanf("%d%d", &n, &m)){
if(!n && !m) break;
printf("%d\n", solve(m) - solve(n-1));
}
return 0;
}
HDU 4734 F(x);
在区间[0, B]内有多少个 F(x) <= F(A).
F(x) = An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.
限制:
1. 先考虑F(x) 的最大值,最高(2^9 - 1) * 9 = 525 * 9
数位DP(大)都是从高位搜下去的,如果按照题意考虑我们持续从最高位搞个中间值加起来!
dp可以记录,当前位是多大的 个数。
但是如何转移呢???对于之前那位,OK,我已经知道之前的和 pre_sum 是多少,
现在我们只要算一下当前pos 满足 F(A) - pre_sum >= 0的那些 DP 值.
考虑减法;
如果对于当前值就是F(A),那么之后的权值和是不是<=F(A)就行了,
是不是变成了当前值是temp,那么之后的权值和是不是<=temp就行了,
那我直接把temp直接记录,就是对于当前位 <= temp 的数量.
注意:
存数位的数组一些位置无意义但被使用,要初始化!
int dp[11][5000];
int a[11];
int n, m;
int DFS(int pos, int sta, bool limit){
if(sta < 0) return 0;
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
int ans = 0;
for(int i=0;i<=up;i++)
ans = ans + DFS(pos-1, sta-(1<<pos)*i, limit && (i == up));
if(!limit) dp[pos][sta] = ans;
return ans;
}
int solve(){
int num = 0;
int temp = 0;
while(n){
temp = temp + (n % 10) * (1<<num);
n /= 10;
num++;
}
memset(a, 0, sizeof(a));
num = 0;
while(m)
{
a[num++] = m % 10;
m /= 10;
}
return DFS(num-1, temp, true);
}
int main(){
int T, cas=1;
scanf("%d", &T);
memset(dp, -1, sizeof(dp));
while(T--){
scanf("%d%d", &n, &m);
printf("Case #%d: %d\n", cas++, solve());
}
return 0;
}
51nod1009 数字1的数量
1-N的数的1的数量;
数位上的思考:
1. 个位上1的数量,不考虑个位限制,也就是意味着区间[1, 9],num0 = 1;
2. 加一个十位 1的数量,不考虑个/十位限制,也就是意味着区间[1, 99],
num1 = (两位数, 1 * 10(十位为1,个位随意[0, 9]) + 1 * 9(个位为1, 十位(1, 9))) + 1(个位数, num0 = 1)
3. 再加一个百位 1的数量,不考虑个/十/百位限制,也就是意味着区间[1, 999]
num2 = (三位数, 1 * 100(百位为1,其余随意[0, 99] + 1 * 90(十位为1, 个位[0, 9], 百位[1, 9]))) + (两位数, num1)
4. 现在直接改称 <= 4 位的一个数[1, 9999],
num4 = 1 * 1000 + 1 * 9 * 100 + 1 * 9 * 100 + 1 * 9 * 100 + num2 = 1*1000 + 3*900 + num2.
显然这还是不够的,
我们还要考虑,
like 区间[1, 36], 显然[1, 9],我们现在要知道的是[10, 36], 但是3>=1, 还是没有限制
like 区间[1, 106], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 106], 百位==1, 总共有6个, 十位只能是0, 个位1的时候,只能是百位为1十位是0
like 区间[1, 236], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 236], 百位==1, 总共有37[0, 36], 个位为1,百位可以 2 1/2, 个位可以[0, 9]
like 区间[1, 216], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 216], 百位==1, 总共有17[0, 16], 十位为1,百位为1, 个位可以10[0, 9], 但是百位为1, 个位只能是7[0, 6]
like 区间[1, 2345], 其实显然[1, 999]我们都知道了, 我们现在要知道[1000, 2345], 千位==1, 总共有346[0, 345],
百位为 1, 千位可以1/2, 后面就是 46 [0, 45]
总结一下???
对于这里我们可以考虑特殊情况然后暴力可以得到。
感觉方法太烂!!!
考虑递推,我们从低到高处理,
答案是累加的过程。
like 区间[1, 36]至36, 对于最高位是1, 都OK就是加[0, 9] 然后考虑尾数1的个数(统筹考虑), 那么就是 +=num1*3([0,1,2]), 之前的就是高位为3的情况
like 区间[1, 106]至106, 对于最高位是1, 那么就是 +=7[0, 06](+尾数+1), 然后加上num2*1(最高位取0)
like 区间[1, 236]至236, 对于最高位是1, 都OK就是加[0, 99], 然后考虑尾数1的个数, 那么就是 +=num2*3([0,1,2]) + 之前的就是高位为3的情况
OK!
也可以考虑DFS,考虑每一位的数的贡献。
CodeForces55D Beautiful numbers
题意:
查询区间有多少个 整除 自己每位上非0的数
限制:
0无限制、能整除每一位。
X % Y == 0
X * 10 % Y == 0
方案1.[GG]
一开始开了临时值然后直接dp[pos]巨撒比,明明这一位对于前一位是有特殊关系的,然后直接后一位求出来了,对于前一位的另一个都不满足了
like -> if(!limit && dp[pos] != -1) return dp[pos];
死于–>转移
方案2.[GG]
想了开状态来记录 [至当前] 能 (%x == 0) 的方案数,但是没用啊!!!
但是中间写崩了————对于中间的时候,很撒比地 如果 [(中间值*10 + i % i) != 0] 直接不管了!!
woc??? 你怎么知道的??? 对于中间的时候 虽然现在不能模i等于0,不代表之后加一些数可以实现膜i等于0吧
死于—>转移
总结一下————
1.显然我们是需要中间值的,用来转移
等等!这个中间好大啊!怎么优化一下,%(123456789的LCM) 就好了嘛.
2.我们无法在中间判断这个值是不是不合法!!!NO WAY!!!所以我们还要记录到个位的时候是不是对所有的位都能膜他等于0
Sooooooooo!!!我们是不是要记录前面这些位,怎么记录————乘起来很棒棒!!!————LCM更加棒棒!!!
直接存 空间复杂度好像很大!!!我们考虑LCM有多少个?(话说怎么求啊)这些LCM (1*2*3*…*8*9) 的约数嘛, 我们可以离散化一下
方案3.
现在我们数位DP, 每次会记录一个中间值temp,一个lcm, 不满足的话 就是到了最低位 temp % lcm != 0;
那么我们开 dp[][][]三维就可以刚了!
int mod, hs[3000], xs[55];
void init(){
mod = 2520;
int num = 0;
for(int i=1;i<=mod;i++)
if(mod % i == 0){
xs[++num] = i;
hs[i] = num;
}
// printf("%d\n", num);
}
int a[25];
LL dp[25][3000][55];
int LCM(int x, int y){
return x * y / __gcd(x, y);
}
LL DFS(int pos, int temp, int lcm, bool limit){
if(pos < 0){
if(temp % lcm == 0) return 1;
return 0;
}
if(!limit && dp[pos][temp][hs[lcm]] != -1) return dp[pos][temp][hs[lcm]];
int up = limit ? a[pos] : 9;
int tmp, tlcm;
LL ans = 0;
for(int i=0;i<=up;i++){
tmp = (temp * 10 + i) % mod;
if(i){
tlcm = LCM(lcm, i);
ans = ans + DFS(pos-1, tmp, tlcm, limit && (i == up));
}
else ans = ans + DFS(pos-1, tmp, lcm, limit && (i == up));
}
if(!limit) dp[pos][temp][hs[lcm]] = ans;
return ans;
}
LL solve(LL n){
int num = 0;
mem(a, 0);
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, 1, true);
}
int main(){
int T;
LL l, r;
init();
mem(dp, -1);
scanf("%d", &T);
while(T--){
scanf("%lld%lld",&l, &r);
printf("%lld\n", solve(r) - solve(l-1));
}
return 0;
}
HDU3555 Bomb
题意:
1-N到有多少个数是包含”49”的.
思路:
还记得不要62嘛…
emmmmm, 我们算一下不要49…然后哼哼哼…
LL dp[25][2];
int a[25];
LL DFS(int pos, int sta, bool limit){
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
LL ans = 0, t;
for(int i=0;i<=up;i++){
if(sta && i == 9) continue;
if(i == 4) t = 1;
else t = 0;
ans = ans + DFS(pos-1, t, limit && (i == up));
}
if(!limit) dp[pos][sta] = ans;
return ans;
}
LL solve(LL n){
int num = 0;
mem(a, 0);
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, true);
}
int main(){
int T;
LL n;
mem(dp, -1);
scanf("%d", &T);
while(T--){
scanf("%lld", &n);
printf("%lld\n", n - solve(n) + 1);
}
return 0;
}
LightOJ 1140
题意:
给出一个区间,求区间内0出现的个数。
思路:
因为窝比较蠢,在想这个特殊位不就是0嘛,ok,状态设个 是0/不是0,感觉美滋滋。
但是显然,这就有一个重大的问题,是的没错,对于当前位,123456789这些数字,他的后多少位出现0的个数都没区别,
但是数位DP,这个DP记录的值表示的是对于当前位的前面所有情况!!!所以太明显了这个思路错的!
所以DP的状态 需要的是到当前位的一类情况。
那么对于这道题,记录从首位到当前位零的个数就对了。
LL dp[25][15];
int a[25];
LL DFS(int pos, bool pre_zero, bool limit, int sta){
if(pos < 0) return (pre_zero ? 1 : (LL)sta);
if(!limit && !pre_zero && dp[pos][sta]!=-1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
LL temp = 0;
for(int i=0;i<=up;i++){
if(pre_zero){
temp += DFS(pos-1, pre_zero&&i==0, limit&&i==up, sta);
}
else{
if(!i) temp += DFS(pos-1, pre_zero, limit && i==up, sta+1);
else temp += DFS(pos-1, pre_zero, limit && i==up, sta);
}
}
if(!limit && !pre_zero) dp[pos][sta] = temp;
return temp;
}
LL solve(LL x){
if(x<0) return 0;
if(x==0) return 1;
int num = 0;
while(x){
a[num++] = x % 10;
x /= 10;
}
return DFS(num-1, true, true, 0);
}
int main(){
LL n, m;
int T, cas = 1;
memset(dp, -1, sizeof(dp));
scanf("%d", &T);
while(T--){
scanf("%lld%lld", &n, &m);
printf("Case %d: ", cas++);
printf("%lld\n", solve(m) - solve(n-1));
}
return 0;
}