您的位置:首页 > 其它

HDU 3555 Bomb ,HDU 2089 深刻学习数位dp (各种方法乱用)

2014-12-26 09:14 471 查看
数位dp是与数位有关的区间统计 参考: 点击打开链接

思想是将数字拆分,dp[i][j] 长度为i的数字有多少个满足j条件的,从000~999

统计时,计当前数字为x,则其后面数字的变化的倍数是0~x-1 后面接000~999这样刚好统计不到这个数字n本身。

自底向上要注意这种方法没有考虑n这个数字本身是否满足要求,所以统计区间 [m,n] 的计算是f(n+1)-f(m)

dfs 状态设计

对必须要有XX数字则用pre保证,否则还是要有tag标记.

那么什么样的前缀对后面子数有相同的结果,余数的妙用对于mod类.state设计用状压

eg:

对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。

例如,在十进制下,计算[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]

一般有两种写法,自底向上和递归+记忆化搜索

/* ***********************************************
Author        :bingone
Created Time  :2014-12-25 13:44:50
File Name     :HDU3555.cpp
************************************************ */

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define inf 0x3f3f3f3f
#define maxn (10+1000)
typedef long long ll;
using namespace std;
int M,T;
ll N,dp[22][3];
int num[22];
ll solve()
{
ll res=0,cache=N;
int tag=0,len=1;
while(cache)
num[len++]=cache%10,cache/=10;
for(num[len--]=0;len;len--){
res+=num[len]*dp[len-1][2];
if(tag==1) {res+=num[len]*dp[len-1][0];continue;}
if(num[len]>4) res+=dp[len-1][1];
if(num[len]==9 && num[len+1]==4) tag=1;
}
return res;
}

int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
dp[0][0]=1;
for(int i=1;i<22;i++){
dp[i][0]=dp[i-1][0]*10-dp[i-1][1];
dp[i][1]=dp[i-1][0];
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];
}
scanf("%d",&T);
while(T--){
scanf("%I64d",&N);N++;
printf("%I64d\n",solve());
}
return 0;
}


其实很多数位dp都是用dfs模板写

typedef long long LL;
const int maxn=22;
int dig[maxn];
LL f[maxn]/* [TODO] */;

LL dfs(int pos,/* TODO */,int limit){
if (pos<0) return /* TODO */;
if (!limit&&f[pos]/* [TODO] */!=-1) return f[pos]/* [TODO] */;
LL res=0;
int last=limit?dig[pos]:9;
for (int i=0;i<=last;i++){
res+=dfs(pos-1,/* TODO */,limit&&(i==last));
}
if (!limit) f[pos]/* [TODO] */=res;
return res;
}

LL solve(LL n){
int len=0;
while (n){
dig[len++]=n%10;
n/=10;
}
return dfs(len-1,/* TODO */,1);
}


于是参考别人的代码这个题有两种dfs

1.按照模板写的,pos表示当前处理到的位置,istrue记录数字处理位置之前的部分是否满足已经满足题意。

limit 该位是否有限制,比如n=1234,现在转移到12,如果下一位选3,那么再下一位就有上限, 上限为4,如果不选3,那么下一位就没限制,最高位9,转移能保证转移到数比n小

这样一来dfs的方法就可以处理n这个数字本身了,因为处理到pos==-1时候istrue已经表明状态。

LL dp
[10][2];
LL dfs(int pos,int pre,int istrue,int limit) {
if (pos < 0) return istrue;
if (!limit && dp[pos][pre][istrue] != -1)
return dp[pos][pre][istrue];
int last = limit ? dig[pos] : 9;
LL ret = 0;
for (int i = 0; i <= last; i++) {
ret += dfs(pos-1,i,istrue || (pre == 4 && i == 9),limit && (i == last));
}///eg : 对于50,0~5 当0的时候判断出来为0则全为0  若没有pre这个存储则是通过其他变量控制但是易出错
if (!limit) {
dp[pos][pre][istrue] = ret;
}
return ret;
}


2.

dp[25][3]和递推方法的状态表达一样。

这个方式就很简单直观了

__int64 Dfs (int pos,int s,bool limit)  //s为之前数字的状态
{
if (pos==-1)
return s==2;
if (limit==false && ~dp[pos][s])
return dp[pos][s];
int i ,end=limit?bit[pos]:9;
__int64 ans=0;
for (i=0;i<=end;i++)
{
int nows=s;
if(s==0 && i==4)
nows=1;
if(s==1 && i!=9)   //前一位为4
nows=0;
if(s==1 && i==4)
nows=1;
if(s==1 && i==9)  //49
nows=2;
ans+=Dfs(pos-1 , nows , limit && i==end);
}
//limit==true 则说明有限制,即所有可能并没有被全部记录
//limit==false则说明之后的分支状态已经搜索完全
return limit?ans:dp[pos][s]=ans;
}


HDU 2089

/* ***********************************************
Author        :bingone
Created Time  :2014-12-24 9:57:30
File Name     :HDU2089.cpp
************************************************ */

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define inf 0x3f3f3f3f
#define maxn (10+1000)
using namespace std;
int N,M,T;
int num[10][3];
int slove(int n)
{
int res=0;
int we[10],tmp,wecnt;
for(wecnt=1,tmp=n;tmp;wecnt++,tmp/=10)
we[wecnt]=tmp%10;
bool tag=false;
for(we[wecnt--]=0;wecnt;wecnt--){
res+=we[wecnt]*num[wecnt-1][2];
if(tag==true){res+=we[wecnt]*num[wecnt-1][0];continue;}
if(we[wecnt]>4)
res+=num[wecnt-1][0];
if(we[wecnt]>6)
res+=num[wecnt-1][1];
if(we[wecnt+1]==6 && we[wecnt]>2)
res+=num[wecnt-1][0];
if(we[wecnt]==4 || (we[wecnt]==2 && we[wecnt+1]==6) )
tag=true;
}
return n-res;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
num[0][0]=1;
for(int i=1;i<7;i++)	/// jili jili and 2 notjili
{
num[i][0]=num[i-1][0]*9-num[i-1][1];
num[i][1]=num[i-1][0];
num[i][2]=num[i-1][2]*10+num[i-1][1]+num[i-1][0];
}
while(~scanf("%d%d",&N,&M)){
if(N==0 && M==0) break;
printf("%d\n",slove(M+1)-slove(N));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: