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

数位dp是与数位有关的区间统计 参考: 点击打开链接

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

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

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]

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

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

/* ***********************************************
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[N][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));
    }
    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;
}
时间: 2024-10-27 02:06:20

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

hdu 3555 Bomb(数位dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意:就是给你一个数n,判断从0到n有多少个数含有数字49...... 是不是觉得跟hdu2089很相似呀... 思路:跟hdu2089一样的,注意给出的数比较大,所以这儿用__int64  .... code: #include<cstdio> #include<iostream> #include<cstring> #include<algorithm&

[ACM] hdu 3555 Bomb (数位DP,统计1-N中含有“49”的总数)

Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 7187 Accepted Submission(s): 2512 Problem Description The counter-terrorists found a time bomb in the dust. But this time the terrorists impro

HDU - 3555 Bomb (数位DP)

题意:求1-n里有多少人包含"49"的数字 思路:数位DP,分三种情况:到第i位没有49的情况,到第i位没有49且最高位是9的情况,到第i位有49的情况,将三种情况都考虑进去就是了 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; long long dp[30][3], n; int arr

[ACM] hdu 2089 不要62(数位Dp)

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 19043    Accepted Submission(s): 6442 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就

HDU 3555——Bomb

HDU 3555——Bomb 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题意:让你找找不大于n的数里有多少含‘49’, 我们发现,T很大,N很大,暴力指定不行,因为含不含'49'是每一位的性质,可以考虑数位dp,(况且这就是数位dp的模板形式啊喂),因为在模板的基础改的,所以找的是1-n有多少个数不含49,总数减一下就行 dp状态记为dp[当前是第几位][前一位是不是4],所以第二维开到2就够用啦 模板题,上代码 1 #include<

HDU 2089 不要62(数位DP,三种姿势)

HDU 2089 不要62(数位DP,三种姿势) ACM 题目地址:HDU 2089 题意: 中文题意,不解释. 分析: 100w的数据,暴力打表能过 先初始化dp数组,表示前i位的三种情况,再进行推算 直接dfs,一遍搜一变记录,可能有不饥渴的全部算和饥渴的部分算情况,记录只能记录全部算(推荐看∑大的详细题解Orz) 代码: 1. 暴力 (以前写的) /* * Author: illuz <iilluzen[at]gmail.com> * File: 2089_bf.cpp * Create

【HDU 4352】 XHXJ&#39;s LIS (数位DP+状态压缩+LIS)

XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2422    Accepted Submission(s): 990 Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then careful

hdu3555 Bomb (记忆化搜索 数位DP)

http://acm.hdu.edu.cn/showproblem.php?pid=3555 Bomb Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 7316    Accepted Submission(s): 2551 Problem Description The counter-terrorists found a time

hud 2089 不要62 (数位dp)

#include<stdio.h> #include<string.h> #include<math.h> #define max 10 int dp[max][3]; int number[max]; //dp[i][0] 前i位数中不符合要求的总个数 //dp[i][1] 前i位数中最高位是2的个数 //dp[i][2] 前i位数中存在含4和有连续62的个数 void init() { memset(dp,0,sizeof(dp)); dp[0][0]=1; for