码迷,mamicode.com
首页 > 其他好文 > 详细

ac1068 数位dp

时间:2014-05-03 23:06:27      阅读:386      评论:0      收藏:0      [点我收藏+]

标签:style   blog   class   code   tar   color   

这题说的是给了一个区间计算这个区间内 数各个数字之和为S的最小数  

 

其实这个题目首先可以求出[A,B]内所有数字之和为S的数的个数cnt,然后观察一下,不难发现,最小的那个数字肯定是在 cnt=1的时候对应的区间端点。由于具有严格的单调性,即随着区间长度的延长,满足条件的cnt肯定会越来越多。所以先可以数位dp求出cnt。然后二 分区间,若cnt>=1,那么说明[A,B]间肯定不止一个符合条件的数字。注意题目要求最小的数字,所以二分区间右端点即可,不要二分左端。这是 主要思路。其实就是个裸的数位dp。

 上面这段话纯属 抄袭  第一次接触数位dp 感觉好神奇

这样说说我的理解  dp[i][j] 表示 第 i 位(这里高位放在尾部) 在前面不管有几位的状态下得到j值 到达第i位时 第i位 该点为最高位 所能形成和为S-j的数有多少个  也等价于记忆化搜索

这里感觉 数位dp这里的 limt 用的很巧 为了判断这个数字是否可以取到极限

bubuko.com,布布扣
#include <iostream>
#include <cstdio>
#include<string.h>
using namespace std;
const int maxn=200;
long long  dp[50][maxn],digit[50],a,b,s;
long long dfs(int pos,int per,int limt){

        if(pos==-1) return per==s;
        if(limt==0&&dp[pos][per]!=-1)return dp[pos][per];
        long long ret=0,ed=limt?digit[pos]:9;
        for(int i=0;i<=ed;++i){
            ret+=dfs(pos-1,per+i,limt&&i==ed);
        }
        if(limt==0) return dp[pos][per]=ret;
        return ret;
}
long long work(long long G){
     int len=0;
     while(G){
        digit[len++]=G%10;
        G=G/10;
     }
   return  dfs(len-1,0,1);
}
bool jud(long long L,long long R){

   return work(R)-work(L)>=1LL;
}
void solve(){
    long long L=a,R=b,mid,L1=a,ans;
    while(L<=R){
        mid=(L+R)/2;
        if(jud(L1,mid)){
            ans=mid;
            R=mid-1;
        }
        else L=mid+1;
    }
    printf("%lld\n",ans);
}
int main()
{
    while(scanf("%lld%lld%lld",&a,&b,&s)==3){
       memset(dp,-1,sizeof(dp));
       solve();
    }
    return 0;
}
View Code

 

ac1068 数位dp,布布扣,bubuko.com

ac1068 数位dp

标签:style   blog   class   code   tar   color   

原文地址:http://www.cnblogs.com/Opaser/p/3705209.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!