SPOJ BALNUM - Balanced Numbers - [数位DP][状态压缩]
题目链接:http://www.spoj.com/problems/BALNUM/en/
Time limit: | 0.123s |
Source limit: | 50000B |
Memory limit: | 1536MB |
Balanced numbers have been used by mathematicians for centuries. A positive integer is considered a balanced number if:
1) Every even digit appears an odd number of times in its decimal representation
2) Every odd digit appears an even number of times in its decimal representation
For example, 77, 211, 6222 and 112334445555677 are balanced numbers while 351, 21, and 662 are not.
Given an interval [A, B], your task is to find the amount of balanced numbers in [A, B] where both A and B are included.
Input
The first line contains an integer T representing the number of test cases.
A test case consists of two numbers A and B separated by a single space representing the interval. You may assume that 1 <= A <= B <= 1019
Output
For each test case, you need to write a number in a single line: the amount of balanced numbers in the corresponding interval
Example
Input: 2 1 1000 1 9
Output: 147 4
题意:
问A到B之间有多少个数,有多少数符合以下条件:
数字0~9,每个奇数出现偶数次, 每个偶数出现奇数次,当然没出现的话无所谓。
题解:
这题可以说是我第一道能自己想出思路来的数位DP了;
看到这个,先开始想,数字0~9,有关出现次数,感觉可以用状压做:0~9一共十位,每位0或者1,代表这个数字出现了偶数次/奇数次……
然后仔细看了一下,发现没法用0/1来记录,因为有一个digit有三个状态:没出现、出现偶数次、出现奇数次;
然后有一瞬间,想到了是不是可以用三进制做,不过没仔细想下去,后来OB了一下题解,发现真可以用三进制状态压缩做。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ll; int dig[20]; ll dp[20][59049]; //dp[pos][sta]:从第len位到第pos+1位各个digit出现情况为sta,剩下的第1位到第pos位任意,共有多少个数字满足题目条件. int digit[59049][10]; //digit[sta][pos]:对于三进制状态压缩sta,它的第pos位是0or1or2. int pow3[11]; //pow3[pos]:记录3的pos次方. void init() { pow3[0]=1; for(int i=1;i<=10;i++) pow3[i]=pow3[i-1]*3; for(int sta=0;sta<pow3[10];sta++) for(int pos=0;pos<=9;pos++) { digit[sta][pos]=(sta/pow3[pos])%pow3[1]; //printf("digit[%d][%d]=%d\n",sta,pos,digit[sta][pos]); } } bool check(int sta) //检查sta是不是满足题目条件 { for(int i=0;i<=9;i++) { if(i%2==1 && digit[sta][i]==1) return 0; if(i%2==0 && digit[sta][i]==2) return 0; } return 1; } int getNewSta(int sta,int x) //更新sta { if(digit[sta][x]<=1) return sta+pow3[x]; else return sta-pow3[x]; } ll dfs(int pos,int sta,bool limit) { if(pos==0) return check(sta); //精确了到某一个数字,检查这个数字是否满足题目条件 if(!limit && dp[pos][sta]!=-1) return dp[pos][sta]; int up=limit?dig[pos]:9; ll ans=0; for(int i=0;i<=up;i++) { if(sta==0 && i==0) ans+=dfs(pos-1,0,limit && i==up); //到第pos位前面全部都是前导零,那么当前第pos位也是0的话,就不记录到sta里 else ans+=dfs(pos-1,getNewSta(sta,i),limit && i==up); //将当前的第pos位记录到sta里 } if(!limit) dp[pos][sta]=ans; return ans; } ll solve(ll x) { int len=0; while(x) { dig[++len]=x%10; x/=10; } return dfs(len,0,1); } int main() { init(); memset(dp,-1,sizeof(dp)); int t; ll A,B; scanf("%d",&t); while(t--) { cin>>A>>B; cout<<solve(B)-solve(A-1)<<endl; } }