题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4722
解题思路:
题目大意:
给你两个数a,b(a<=b),问你a~b满足各个数位相加能整除10的个数,含边界。
算法思想:
先枚举一下0~200内满足条件的值,0,19,28,37,46,55,64,73,82,91,109,118,127,136,145,154,163,172,181,190.规律很显然就出来了,0~10中有一个,10~20中有一个。。。可知:每十个中必有一个(自己可以在纸上画一下)。一个很大的数共有n位,前面n-1位的和是p而最后一位还没确定,最后一位可以取q=0~9,自己可以想一下结果肯定是(p+q)%10,而模上10以后的结果必是从0~9,所以每十个里面必有一个。接下来的就好办了,问你a~b满足条件的个数,就先求出边界c~d(c<=a且最大的满足条件的数,d<=b且最大的满足条件的数),最后结果就是(d-c)/10。
AC代码(找规律);
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
bool judge(ll x){
int sum = 0;
while(x){
sum += x%10;
x /= 10;
}
if(sum % 10 == 0)
return true;
else
return false;
}
int main(){
int T,t = 1;
scanf("%d",&T);
while(T--){
ll a,b;
scanf("%lld%lld",&a,&b);
a--;
while(!judge(a))
a--;
while(!judge(b))
b--;
printf("Case #%d: %lld\n",t++,b/10-a/10);
}
return 0;
}
AC代码(记忆化搜索):
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 25;
ll dp[maxn][12];
ll digit[maxn];
ll a,b;
ll dfs(int pos,int pre,bool limit){//pre表示前面各位数字之和对该数取模的结果
if(pos == -1)
return pre == 0;
if(!limit && dp[pos][pre]!=-1)
return dp[pos][pre];
ll res = 0,tmp = limit?digit[pos]:9;
for(int i = 0; i <= tmp; i++){
int new_pre = (pre+i)%10;
res += dfs(pos-1,new_pre,limit&&i==tmp);
}
if(!limit)
dp[pos][pre] = res;
return res;
}
ll solve(ll n)
{
int len=0;
while(n){
digit[len++] = n%10;
n /= 10;
}
return dfs(len-1,0,true);
}
int main(){
int T;
scanf("%d",&T);
for(int i = 1; i <= T; i++){
memset(dp,-1,sizeof(dp));
scanf("%lld%lld",&a,&b);
printf("Case #%d: %lld\n",i,solve(b)-solve(a-1));
}
return 0;
}
AC代码(数位dp):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll a,b,dp[20][10];
ll v[20],vn;
void solve2(ll num){
vn = 0;
while(num){
v[++vn] = num % 10;
num /= 10;
}
for(int i = 1; i <= vn / 2; i++)
swap(v[i], v[vn - i + 1]);
}
ll solve(ll num){
if (num == -1)
return 0;
memset(dp, 0, sizeof(dp));
solve2(num);
int x = 0;
for (int i = 1; i <= vn; i++){
for (int j = 0; j < 10; j++){
for (int k = 0; k < 10; k++){
dp[i][(j + k) % 10] += dp[i - 1][j];
}
}
for (int j = 0; j < v[i]; j++) {
dp[i][(x + j) % 10]++;
}
x = (x + v[i]) % 10;
}
if (!x)
dp[vn][0]++;
return
dp[vn][0];
}
int main(){
int T,t = 1;
scanf("%d", &T);
while(T--){
scanf("%lld%lld",&a,&b);
printf("Case #%d: %lld\n",t++,solve(b)-solve(a-1));
}
return 0;
}