[HDU 2451] Simple Addition Expression (组合数学 或 数位DPSimple Addition Expression)

Simple Addition Expression

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2451


题目大意:

  有一种计算器,专门用来计算连续的三个数的和,不过这个计算器比较低端,不能够进位。如 0 + 1 + 2 ,计算器算出3,但 3 + 4 + 5 计算器只能算出 2。正确答案是12,由于无法进位。把1直接给扔了。
  现在给你一个数N,求 [0,N) 的范围内有多少个数,可以作为连续三个数的第一个数,使得计算器能够算出正确答案。

解题思路:

  我的思路是这样的,由于是组合数学专题的题目,于是我就想朝着这方面靠。(然后发现自己的姿势总是不够优美)
  
  首先分析一位数,这个数只能为0,1,2. 
  其次分析两位数,个位数同上,而十位数可以为1,2,3。
  接着分析三位数,个位数同上,十位数可以为0,1,2,3.。而百位数可以为1,2,3.
  于是不难发现,对于一个N位数,最高位有3种选择,个位有三种选择,其余位有4种选择。 即 3 * 4 * 4*……* 4 * 3 (N >= 3)
  然后我就直接开始看这个数为几位数,如果为N位数,那么从 1 位到 N - 1 位的都很好算。接下来就是算N位数了,而判断N位数就很麻烦了,需要从最高位一个一个推过来,如果最高位是小于等于3的,为k。那么当最高位取1到k - 1时,后面的位数同上,但当最高位取了k时,又要考虑下一位的取值。以此类推,十分麻烦,然后我放弃了,因为发现与其这样不如直接用数位DP来一发。。

  数位DP的解法。具体的方法详见代码吧。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 1000
#define INF 1<<25
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
using namespace std;
int pos, bit[12];
void open(ll n)
{
    pos = -1;
    while(n)
    {
        bit[++pos] = n % 10;
        n /= 10;
    }
}
int f[12][4];
ll dfs(int pos, int s, bool e)
{
    if (pos == -1) return (s != 3); //如果搜到最后一位,即各位的时候,要把 3 给去掉。即s == 3 的时候,return 0
    if (!e && f[pos][s] != -1) return f[pos][s];
    ll res = 0;
    int u = e ? bit[pos] : 3;
    if (u > 3) u = 3; //之前没加这句话,wa了。其实是这样的,u如果等于bit[pos]的话,有可能大于3,而我们根本不需要考虑大于3的情况。要不然就在搜索的时候多算了
    for (int d = 0; d <= u; d++)
    {
        res += dfs(pos - 1, d, e && d == bit[pos]);
    }
    return e ? res : f[pos][s] = res;
}
void solve(ll n)
{
    mem(f, -1);
    cout<<dfs(pos, 0, 1)<<endl;
}
int main ()
{
    ll n;
    while(~scanf("%I64d", &n))
    {
        n--;
        open(n);
        solve(n);
    }
    return 0;
}


  后来看了别人的结题报告,发现自己弱爆了。。
  其实我完全没必要把数字分为 1 到 N 位来做,直接用N位数来考虑,如果前面有零,不就是位数减少了么。
int main ()
{
    ll n;
    while(cin>>n)   //hdu当中long long 类型不能用%lld输入。。给跪了 (第二次被坑了)
    {
        open(n);
        ll ans = 0;
        bool flag = false;
        for (int i = pos; i >= 1; i--)
        {
            if (bit[i] > 3) flag = true;
            if (flag) //如果出现某一位大于3,就不用继续循环了
            {
                ans += 3 * pow(4.0, i); //直接把从该位开始的所有情况都加上,并且除了各位,每一位都能取0,1,2,3 四种情况。
                break;
            }
            ans += bit[i] * 3 * pow(4.0, i - 1); //当bit[i]小于等于3的时候,该位只能去0,1,……bit[i]。
        }
        if (!flag) ans += (bit[0] > 3 ? 3 : bit[0]); //这一步很重要,如果flag为false,及代表着前面都没有bit[i]大于3,就需要判断个位
        cout<<ans<<endl;
    }
    return 0;
}


  
  
  

你可能感兴趣的:(组合数学,排列组合)