首页 > 代码库 > F(x)
F(x)
F(x)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4734
参考博客:http://www.cnblogs.com/zinthos/p/3899606.html
数位dp
这题用艾神教的数位dp方法好像过不了,于是我去网上找了下数位dp的做法(orz艾神自创算法)
对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。 此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。 例如,在十进制下,计算[10000,54321]内的数字和, 我们可以将其分解为: [10000,19999],[20000,29999],[30000,39999], [40000,49999],[50000,54321]。 前四个区间如果可以直接得到,则只需处理最后一个区间, 进一步将最后一个区间划分为: [50000,50999],[51000,51999],[52000,52999], [53000,53999],[54000,54321]。 同理将最后一个区间划分下去,最后可以得到以下区间划分: [10000,19999],[20000,29999],[30000,39999], [40000,49999],[50000,50999],[51000,51999], [52000,52999],[53000,53999],[54000,54099], [54100,54199],[54200,54299],[54300,54309], [54310,54319],[54320,54321] 于是,我们只需要预处理除[54320,54321]的区间的值, 再暴力[54320,54321]区间,那么即可得到结果。 这个过程只需要用到记忆化搜索。
于是,数位dp就有了固定的解题思路:
1 typedef long long LL; 2 const int maxn=22; 3 int dig[maxn]; 4 LL f[maxn]/* [TODO] */; 5 6 LL dfs(int pos,/* TODO */,int limit){ 7 if (pos<0) return /* TODO */; 8 if (!limit&&f[pos]/* [TODO] */!=-1) return f[pos]/* [TODO] */; 9 LL res=0; 10 int last=limit?dig[pos]:9; 11 for (int i=0;i<=last;i++){ 12 res+=dfs(pos-1,/* TODO */,limit&&(i==last)); 13 } 14 if (!limit) f[pos]/* [TODO] */=res; 15 return res; 16 } 17 18 LL solve(LL n){ 19 int len=0; 20 while (n){ 21 dig[len++]=n%10; 22 n/=10; 23 } 24 return dfs(len-1,/* TODO */,1); 25 }
对于这道题来说,当前位上的数字对下一个状态来说是无关紧要的,重要的是填了这个数后剩下还能填多少值,
于是可以定义状态:dp[idx][res]表示当前为第idx位剩余数值为res的情况数。
当idx<0&&res>=0时,该状态合法;
当res<0时,状态非法。
代码如下:
1 /*苟利国家生死已,岂因祸福避趋之*/ 2 #include<cstdio> 3 #include<cstring> 4 #define I 10 //位数 5 #define RES 4600 //rest 6 using namespace std; 7 int T,A,B,dp[I][RES],ans,dig[I],bit[I]; 8 int dfs(int idx,int res,bool flag){ 9 if(idx<0)return res>=0; 10 if(res<0)return 0; 11 if(!flag&&dp[idx][res]!=-1)return dp[idx][res]; 12 int temp=0,last=flag?dig[idx]:9; 13 for(int i=0;i<=last;++i) 14 temp+=dfs(idx-1,res-i*bit[idx],flag&&i==last); 15 if(!flag)dp[idx][res]=temp; 16 return temp; 17 } 18 int solve(){ 19 int n=-1,res=0; 20 while(B){ 21 dig[++n]=B%10; 22 B/=10; 23 } 24 for(int i=0;A;A/=10,++i) 25 res+=bit[i]*(A%10); 26 return dfs(n,res,1); 27 } 28 void init(){ 29 memset(dp,-1,sizeof(dp)); 30 for(int i=0;i<10;++i) 31 bit[i]=1<<i; 32 } 33 int main(void){ 34 init(); 35 scanf("%d",&T); 36 for(int times=1;times<=T;++times){ 37 scanf("%d%d",&A,&B); 38 ans=solve(); 39 printf("Case #%d: %d\n",times,ans); 40 } 41 }
总结:
艾神的算法是给所有参数都创建一个维度,直接递推得到答案,当询问条件改变时,无法使用之前得到的值。适合询问次数少的情况。
而记忆化搜索是预处理各个区间的值,最后暴力小区间,适用于询问次数较多的情况。
F(x)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。