Palindromic Numbers(數位dp)
A palindromic number or numeral palindrome is a 'symmetrical' number like 16461 that remains the same when its digits are reversed. In this problem you will be given two integers i j, you have to find the number of palindromic numbers between i and j (inclusive).
InputInput starts with an integer T (≤ 200), denoting the number of test cases.
Each case starts with a line containing two integers i j (0 ≤ i, j ≤ 1017).
OutputFor each case, print the case number and the total number of palindromic numbers between i and j (inclusive).
Sample Input
4
1 10
100 1
1 1000
1 10000
Sample Output
Case 1: 9
Case 2: 18
Case 3: 108
Case 4: 198
雖然說是數位dp,但是我的做法卻是不倫不類的,但是自己感覺還是蠻有趣的,就在這裡記錄一下吧;
題目大意:
給你i,j求兩個數之間迴文數的個數,注意前導零;
思路:
一.既然是求i,j之間的個數,還是老套的求f(j)-f(i-1);
二.利用dp[i]表示所有i位的數有多少迴文數(例如i=3時,儲存100到999之間的迴文數),這也是我用到的全部數位dp了;
那麼要如何儲存呢,分奇數位和偶數位兩種情況:
偶數位如果保證迴文,要求對稱,如果是i位數,前i/2位要對稱於後i/2位,
例如四位數,前2位數字有10到99共有90種情況,所以四位數共有90個迴文數;
奇數位相當於在少一位的偶數位中間插入一個數,這個數可以是0到9共10個數任意一個;
例如五位數,中間不考慮就是有90種情況,這時考慮中間數有10種,所以共有90*10種情況;
得到遞推公式:
if(i%2)
dp[i]=dp[i-1]*10;
else
dp[i]=num[i/2];
ps:int num[20]={0,9,90,900,9000,90000,900000,9000000,90000000};
因為num知道,所以直接寫了個數組存;
注意0也是迴文數,所以dp[1]=10;
三.分步求結果,感覺更像數學公式,這裡拿34512這個數字舉例子:
1.利用遞推公式可知0~9999共有dp[1]+dp[2]+dp[3]+dp[4]個;
2.開始計算10000~34512;
(1)先拆分34512為34 5 12;
(2)則前兩位為10~33時共33-9種情況時共有(33-9)*10個迴文數,此時中間數可以為0~9,後兩位可以是任何組合;
(3)前兩位為34時,考慮中間數,有0~4共5種情況,此時後兩位可以是任何組合;
(4)前兩位為34,中間為5時,比較最後兩位與前兩位的對稱數,因為此時只有34543是迴文數,但是12比43小,達不到43,那麼這種情況就不算;
3.於是最後結果就是dp[1]+dp[2]+dp[3]+dp[4]+(33-9)*10+5+0,四步合一得到最終結果;
4.偶數位同理,但是不考慮中間數罷了;
程式碼(程式碼很雜亂,思路也不算清晰,寫到哪裡想到哪裡,不打推薦看程式碼,容易繞暈,但是隻要掌握上面方法就很容易弄清了):
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
int num[20]={0,9,90,900,9000,90000,900000,9000000,90000000};
int first0[20]={0,0,9,99,999,9999,99999,999999,9999999}; //多餘的含前導零數
LL dp[20];
struct huiwen
{
int l,fl,mid,r,flag,len;//flag=0為偶數 =1為奇數 fl儲存的是前幾個數的對稱數
}h;
LL solve()
{
LL i,sum=0;
for(i=1;i<h.len;i++)
{
sum+=dp[i];
}
if(h.flag)
{
sum+=((h.l-1-first0[h.len/2])*10+h.mid);
if(h.fl<=h.r)
sum++;
}
else
{
sum+=(h.l-1-first0[h.len/2]);
if(h.fl<=h.r)
sum++;
}
return sum;
}
int putin(LL x)
{
int a[20],i=0,m;
while(x)
{
a[++i]=x%10;
x/=10;
}
m=(i+1)/2;
h.l=h.r=h.fl=0;
h.len=i;
if(i%2)
{
h.flag=1;
h.mid=a[m];
for(;i>m;i--)
h.l=(h.l*10+a[i]);
for(i=m+1;i<=h.len;i++)
h.fl=(h.fl*10+a[i]);
for(i=m-1;i>=1;i--)
h.r=(h.r*10+a[i]);
}
else
{
h.flag=0;
for(;i>m;i--)
h.l=(h.l*10+a[i]);
for(i=m+1;i<=h.len;i++)
h.fl=(h.fl*10+a[i]);
for(i=m;i>=1;i--)
h.r=(h.r*10+a[i]);
}
return 0;
}
int main()
{
int i,t,ca=1;
LL a,b;
dp[1]=10;
for(i=2;i<=17;i++)
{
if(i%2)
dp[i]=dp[i-1]*10;
else
dp[i]=num[i/2];
}
cin>>t;
while(t--)
{
cin>>a>>b;
if(a>b)
swap(a,b);
if(b<10)
b++;
else
{
putin(b);
b=solve();
}
if(a>10)
{
putin(a-1);
a=solve();
}
cout<<"Case "<<ca++<<": "<<b-a<<endl;
}
}
看了網上大觸的做法,感覺我這個比起來要繁瑣很多,但是比較容易想明白,偏低思考的做法Orz。