[数位dp] hdu 3886 Final Kichiku “Lanlanshu”

题意:

在范围内满足所给的运算符的数有多少个。

“/” 代表前面的比后面的小 “-”代表前面和后面一样 “\” 代表前面的比后面的大

测试过会出现重复的符号 比如 "///\" 

思路:

细心啊细心啊。。!!

数位dp,按位dp

注意几个点就好了:

1、n个运算符至少要有n+1个数。

2、注意开始的状态,第一个数以及第一个运算符。

3、运算符后移注意移动到结束标识就直接返回0。

4、运算符能往后移就往后移。

5、注意大数减一的计算。

6、注意减法的取模。

代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
using namespace std;
//2014年9月26日12:49:21
__int64 dp[102][102][12];
int num[102],m=100000000;  //因为8位数 所以对100000000取模
char v[102];
int ok(int n,int y,int z)
{
    if(n>=101) //特判101
    {
        if(n==101) n=0;
        else return 0;
    }
    if(v[n]=='\0') return 0; //注意如果字符串结束了 返回0
    char x=v[n];
    if(x=='/') return y<z;
    else if(x=='-') return y==z;
    else return y>z;
}
__int64 dfs(int site,int n,int cur,int zero,int f)  //位数,运算符,前一个数,前导零,是否是边界
{
    if(site==0)
    {
        if(zero) return 0;
        int tep=strlen(v)-1;  //是否取到了最后一位
        return tep==n;
    }
    if(!f&&!zero&&~dp[site][n][cur]) return dp[site][n][cur];
    int len=f?num[site]:9;
    int ans=0;
    for(int i=0; i<=len; i++)
    {
        if(zero)
        {
            if(i==0) ans+=dfs(site-1,n,cur,zero&&i==0,f&&i==len);
            else
            {
                if(cur==10) ans+=dfs(site-1,101,i,zero&&i==0,f&&i==len);  //首先第一位直接放进去 然后因为只有一个数还不能确定运算符
                else
                {
                    if(ok(n+1,cur,i)) ans+=dfs(site-1,n+1,i,zero&&i==0,f&&i==len);  //这里的原则是运算符能往后走就往后走
                    else if(ok(n,cur,i)) ans+=dfs(site-1,n==101?0:n,i,zero&&i==0,f&&i==len); //保持原样,然后就是101代表第二个数 因为-1会越界
                }
            }
        }
        else
        {
            if(cur==10) ans+=dfs(site-1,101,i,zero&&i==0,f&&i==len);
            else
            {
                if(ok(n+1,cur,i)) ans+=dfs(site-1,n+1,i,zero&&i==0,f&&i==len);
                else if(ok(n,cur,i)) ans+=dfs(site-1,n==101?0:n,i,zero&&i==0,f&&i==len);
            }
        }
        ans%=m;
    }
    if(!f&&!zero) dp[site][n][cur]=ans;
    return ans;
}
__int64 solve(char *x,int f)  //f用来区分是否要减一
{
    int i=0,cnt=0;
    int len=strlen(x);
    while(x[i]=='0') i++;  //去前导0
    if(x[i]=='\0')  return 0;  //如果是0
    for(int j=len-1; j>=i; j--) num[++cnt]=x[j]-'0'; //放入num
    if(f) //减一
    {
        num[1]--;
        for(int j=1; j<=cnt; j++)
        {
            if(num[j]<0)
            {
                num[j]+=10;
                num[j+1]--;
            }
        }
    }
    if(num[cnt]==0) cnt--; //注意退位
    return dfs(cnt,101,10,1,1);
}
int main()
{
    while(scanf("%s",v)!=-1)
    {
        memset(dp,-1,sizeof(dp));
        char x[123],y[123];
        scanf("%s%s",x,y);  //比较长 要用字符串读入
        printf("%08I64d\n",(solve(y,0)-solve(x,1)+m)%m); //减法一定注意取模。。
    }
    return 0;
}
//2014年9月26日19:08:24


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值