之前做數位統計時,一般是先初始化,然后再逐位統計。 前幾天在codeforces遇到一種用記憶化搜索寫的數位統計,挺神奇的。用它改寫之前寫過的幾道數位統計,發現代碼更短,速度也更快,有一定通用性. 這種寫法一般用于初始化跟逐位統計是一樣過程的。 codeforces 55D http://www.shnenglu.com/Yuan/archive/2011/01/24/139201.html 我是從這題了解到這種寫法 用這種方法寫,一個流程是,列出式子(pre*10^pos + next) pre是確定的,next是變量 所以參數就為pre,pos加上一些其他的,還有一個標記doing,表示是計算有上界限制的還是沒有上界限制(所有情況,即end=9) dfs(pos-1 , npre , doing && i ==end) 2010成都賽區A題 zoj 3416 之前的寫法 http://www.shnenglu.com/Yuan/archive/2010/11/14/133608.html 改寫后
 /**//*
題目描述見另外一份
平衡,即∑a[i]*(i-o) = 0 o為支點
對于一個數,支點o是唯一的,所以不會有重復計數的情況(但有一種特殊的,就是0,00,000等都是一樣的,會計算多次,
最后減去即可)
假設檢查到pos處,對于上面的式子∑a[i]*(i-o) = 0,這里確定了支點為o
之前的數其∑a[i]*(i-o)的結果為pre
所以參數需要為pos , o , pre

當檢查到pos=-1時,return pre == 0
否則,看當前是計算所有情況還是具體情況(doing)
如果是所有情況且dp值!=-1,直接return
否則就枚舉0到end

而支點o需要在最外層枚舉出來
*/
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

long long dp[19][19][2000];
int digit[19];

void init()
  {
memset(dp,-1,sizeof(dp));
}

long long dfs(int pos , int o , int pre , bool doing)
  {
if(pos == -1)
return pre == 0;

if(!doing && dp[pos][o][pre] != -1)
return dp[pos][o][pre];

long long ans = 0;
int end = doing ? digit[pos] : 9;
for(int i = 0 ; i <= end ; i ++)
 {
int npre = pre;
npre += (pos-o)*i;
ans += dfs(pos-1 , o , npre , doing && i == end);
}

if(!doing)
dp[pos][o][pre] = ans;
return ans;
}

long long cal(long long x)
  {
int pos = 0;
while(x)
 {
digit[pos++] = x % 10;
x /= 10;
}
long long ans = 0;
for(int o = 0 ; o < pos ; o ++)
 {
ans += dfs(pos-1 , o , 0 , 1);
}
return ans - (pos-1);//duplicate 0
}

int main()
  {
init();
int T;
for(scanf("%d",&T) ; T--; )
 {
long long left , right;
scanf("%lld%lld",&left , &right);
printf("%lld\n",cal(right) - cal(left - 1));
}
return 0;
}
2010 成都網絡賽 B hdu 3652 之前的代碼 http://www.shnenglu.com/Yuan/archive/2010/09/23/127454.html
 /**//*
求[1,n]內有多少個數字,該數字有13,且能被13整除 n<=10^9
x % 13 = 0
(pre*10^pos + next) % 13 = 0 pre是之前確定的部分
需要的參數為pre , pos , 狀態have
have記錄pre擁有"13",pos+1位為"1",沒有"13" 分別用have = 2,1,0表示
然后記憶化搜索

*/
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int dp[10][13][3];
int digit[10];

__int64 dfs(int pos , int pre , int have , bool doing)
  {
if(pos == -1)
return have == 2 && pre == 0;

if(!doing && dp[pos][pre][have] != -1)
return dp[pos][pre][have];

int ans = 0;
int end = doing ? digit[pos] : 9;
for(int i = 0 ; i <= end ; i ++)
 {
int npre = (pre*10 + i) % 13;
int nhave = have;
if(have == 0 && i == 1)
nhave = 1;
else if(have == 1 && i != 1)
nhave = 0;
if(have == 1 && i == 3)
nhave = 2;
ans += dfs(pos-1 , npre , nhave , doing && i == end );
}

if(!doing)
dp[pos][pre][have] = ans;
return ans;
}


int cal(int x)
  {
int pos = 0;
while(x)
 {
digit[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1 , 0 , 0 , 1);
}

int main()
  {
memset(dp,-1,sizeof(dp));
int n;
while(~scanf("%d",&n))
printf("%d\n",cal(n));
return 0;
}
參數跟條件有關,就像hdu3555就不需要pre
hdu 3555 之前寫法 http://www.shnenglu.com/Yuan/archive/2010/10/24/131057.html
 /**//*
問[1,n]內有多少個數字包含"49"
這里用記憶化搜索
更加好寫,而且快

hdu 3652 是加強版
*/
#include<cstdio>
#include<algorithm>

using namespace std;


__int64 dp[20][3];
int digit[20];

__int64 dfs(int pos , int have , bool doing)
  {
if(pos == -1)
return have == 2;

if(!doing && dp[pos][have] !=-1)
return dp[pos][have];

__int64 ans = 0;
int end = doing ? digit[pos] : 9;
for(int i = 0 ; i <= end; i++)
 {
int nhave = have;
if(have == 1 && i != 4)
nhave = 0;
if(have == 0 && i == 4)
nhave = 1;
if(have == 1 && i == 9)
nhave = 2;
ans += dfs(pos-1 , nhave , doing && i == end);
}
if(!doing)
 {
dp[pos][have] = ans;
}

return ans;
}

__int64 cal(__int64 x)
  {
int pos = 0;
while(x)
 {
digit[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1 , 0 , 1);
}

int main()
  {
memset(dp,-1,sizeof(dp));
int T;
for(scanf("%d",&T) ; T-- ;)
 {
__int64 n;
scanf("%I64d",&n);
printf("%I64d\n",cal(n));
}
return 0;
}
|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|