ural 1057 Amount of Degrees (数位DP)
详见:刘聪的论文,写的很详细......
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int f[35][35]; //前 i 位有 j 个 1 的个数...
int x,y,b;
int pos[35];
int len;
void init() { //预处理
memset(f,0,sizeof(f));
f[0][0] = 1;
for(int i=1; i<=32; i++) {
f[i][0] = f[i-1][0];
for(int j=1; j<=i; j++) {
f[i][j] = f[i-1][j-1] + f[i-1][j];
}
}
}
int solve(int t,int k) {
len = 1;
while(t) {
pos[len++] = t % b;
t /= b;
}
int ans = 0;
for(int i=len-1; i>=1; i--) {
if(pos[i] > 1) {
ans += f[i-1][k] + f[i-1][k-1];
break;
} else if(pos[i] == 1) {
ans += f[i-1][k];
k --;
}
if(k < 0) break;
}
return ans;
}
int main() {
init();
int k;
while(scanf("%d%d%d%d",&x,&y,&k,&b) != EOF) {
printf("%d\n",solve(y+1,k) - solve(x,k));
}
return 0;
}
hdu 2089 不要62
题意:找出【x,y】区间内,满足不存在4且不存在62的数的个数
先预处理出前i位,j作为最高位时符合要求的数的个数。然后对于0--n的总个数,从高位到低位,枚举小于n的每一位。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
int f[11][11];
int n,m;
int pos[11];
void init() {
memset(f,0,sizeof(f));
f[0][0] = 1;
for(int i=1; i<7; i++) {
for(int j=0; j<=9; j++) {
if(j == 4) continue;
for(int k=0; k<=9; k++) {
if(j == 6 && k == 2) continue;
f[i][j] += f[i-1][k];
}
}
}
}
int solve(int x) {
memset(pos,0,sizeof(pos));
int len = 0;
int flag = 1;
while(x) {
pos[++len] = x % 10;
if(pos[len] == 4 || (pos[len] == 6 && pos[len-1] == 2)) flag = 0;
x /= 10;
}
int ans = 0;
if(flag) ans ++;
for(int i=len; i>=1; i--) {
for(int j=0; j<pos[i]; j++) {
if(j == 4) continue;
if( i < len && pos[i+1] == 6 && j == 2) continue;
ans += f[i][j];
}
if(pos[i] == 4 || (i < len && pos[i+1] == 6 && pos[i] == 2)) break;
}
return ans;
}
int main() {
init();
while(scanf("%d%d",&n,&m)) {
if(n == 0 && m == 0) break;
printf("%d\n",solve(m) - solve(n-1));
}
return 0;
}
递归写法:
int dp[10][3];
int pos[10];
int dfs(int p,int buff,int flag) {
if(p == -1) return (buff == 0 || buff == 1);
if(flag == 0 && dp[p][buff] != -1) return dp[p][buff];
int ans = 0;
int end = flag ? pos[p] : 9;
for(int i=0; i<=end; i++) {
if(i == 4 || buff == 2 || (i == 2 && buff == 1)) ans += dfs(p-1,2,(flag && i == end));
else if(i == 6) ans += dfs(p-1,1,(flag && i == end));
else ans += dfs(p-1,0,(flag && i == end));
}
if(!flag) dp[p][buff] = ans;
return ans;
}
int solve(int x) {
int len = 0;
while(x) {
pos[len++] = x % 10;
x /= 10;
}
return dfs(len - 1,0,1);
}
int main() {
memset(dp,-1,sizeof(dp));
int n,m;
while(scanf("%d%d",&n,&m) && (n + m)) {
printf("%d\n",solve(m) - solve(n-1));
//printf("%d\n",m - solve(m) - (n - 1 - solve(n - 1)));
}
return 0;
}
hdu 3652 B-number
此题求的是存在连续的‘13’且能被13整除的数的个数
试试递归写法...记忆化dp
#include <iostream>
#include <algorithm>
#include <cmath>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
using namespace std;
int dp[15][15][15][2];
int pos[15];
int dfs(int p,int pre,int mod,int buff,bool flag) {
// p 表示当前处理位置, pre 表示前驱数字, buff 为目标状态(此题包括mod == 0), flag 表示此位置是否能枚举任意数字
if(p == -1) return (buff && !mod);
if(flag == 0 && dp[p][pre][mod][buff] != -1) return dp[p][pre][mod][buff];
int ans = 0;
int end = flag ? pos[p] : 9;
for(int i=0; i<=end; i++) {
if(pre == 1 && i == 3) ans += dfs(p - 1,i,(mod * 10 + i) % 13,1,(flag && i == end));
else ans += dfs(p - 1,i,(mod * 10 + i) % 13,buff,(flag && i == end));
}
if(!flag) dp[p][pre][mod][buff] = ans;
return ans;
}
int solve(int n) {
int len = 0;
while(n) {
pos[len++] = n % 10;
n /= 10;
}
return dfs(len - 1,0,0,0,1);
}
int main() {
int n;
memset(dp,-1,sizeof(dp));
while(scanf("%d",&n) != EOF) {
printf("%d\n",solve(n));
}
return 0;
}
poj 3252 Round Numbers
在区间【l,r】内的数当二进制表示时,求出满足条件的个数,条件为:0的个数大于等于1的个数。
需要考虑前导0
#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
using namespace std;
int pos[40];
int dp[40][40][40];
int len ;
int dfs(int p,int num0,int num,int flag,int first) {
if(p == -1) {
if(num0 >= num) return 1;
return 0;
}
if(flag == 0 && dp[p][num0][num] != -1) return dp[p][num0][num];
int ans = 0;
int end = flag ? pos[p] : 1;
for(int i=0; i<=end; i++) {
if(first) {
if(i == 0) ans += dfs(p-1,num0,num,(flag && i == end),1);
if(i == 1) ans += dfs(p-1,num0,num + 1,(flag && i == end),0);
} else {
if(i == 0) ans += dfs(p-1,num0 + 1,num,(flag && i == end),0);
if(i == 1) ans += dfs(p-1,num0,num + 1,(flag && i == end),0);
}
}
if(!flag) dp[p][num0][num] = ans;
return ans;
}
int solve(int x) {
len = 0;
while(x) {
pos[len ++] = x % 2;
x /= 2;
}
return dfs(len - 1,0,0,1,1);
}
int main() {
int n,m;
memset(dp,-1,sizeof(dp));
// while(scanf("%d",&n) != EOF) {
// printf("%d\n",solve(n));
// }
scanf("%d%d",&n,&m);
printf("%d\n",solve(m) - solve(n-1));
return 0;
}
hdu 3709 balanced number
在[x,y]区间内求满足条件的数,条件是该数以某一位为轴心,其他位的权值为数位值 * 偏移量 ,要求轴心左边与右边权值和相等。
#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define REP(i,s,t) for(int i=(s);i<=(t);++i)
#define ll __int64
using namespace std;
int pos[20];
ll dp[20][20][2222];
ll dfs(int p,int mid,int sum,int flag) {
if(p == -1) return sum == 0;
if(sum < 0) return 0;
if(flag == 0 && dp[p][mid][sum] != -1) return dp[p][mid][sum];
int end = flag ? pos[p] : 9;
ll ans = 0;
for(int i=0; i<=end; i++) {
ans += dfs(p - 1,mid,sum + (p - mid) * i, (flag && i == end));
}
if(!flag) return dp[p][mid][sum] = ans;
return ans;
}
ll solve(ll x) {
int len = 0;
while(x) {
pos[len++] = x % 10;
x /= 10;
}
ll ans = 0;
for(int i=0; i<len; i++) {
ans += dfs(len-1,i,0,1);
}
return ans - len + 1; // 减去前导零的情况
}
int main() {
memset(dp,-1,sizeof(dp));
int T;
scanf("%d",&T);
ll n,m;
while(T--) {
scanf("%I64d%I64d",&n,&m);
printf("%I64d\n",solve(m) - solve(n-1));
}
return 0;
}