vijos P2026 几乎奇偶等和数 数位dp 好题

来源:互联网 发布:html5上传文件表单php 编辑:程序博客网 时间:2024/06/05 16:45

题意:
我们称一个整数是奇偶等和数,是说它的数位个数是偶数(比如二位数,四位数,六位数等,且特殊说明 0 是一位数字),且其中所有奇数位数字之和恰好等于所有偶数位数字之和。
我们称一个整数是几乎奇偶等和数,是说在恰好修改其中一位数字后,得到的新数字是一个奇偶等和数。这里说恰好修改一位数字,要求必须发生了实质性修改,也就是说修改后的数字必须与之前的数字不同(例如将1234修改为1234就是不合法的,因为修改后的数字和之前是一样的);同时要求不能将最高位修改为 0。
现在给定整数 A和 B,问有多少几乎奇偶等和数 x 满足 A≤x≤B。

思路:
首先明确这个题目一定要修改某一位才行,也就是说原本的奇偶位的和不等,并且数位长度为偶数,那么肯定对于这些东西我们都需要开一个变量来记录的.
难点在于怎么修改,我们可以这样考虑.考虑偶数为的和跟奇数位和的差值,那么如果当前差为正值,说明偶数位和大,那么我们需要修改奇数某一位,可以发现修改某一位的值他的差值变化是在一个范围之内的,每次要么把最小的修改成最大的,或者把最大的修改成最小的(注意首位不能为0),这样就可以使差值在一个范围然后判断一下就好了.奇数位和大同理.
注意奇数位变小使得差值变大,变大使得差值变小,偶数位反之.

#include<bits/stdc++.h>using namespace std;const int maxn = 1e5+5;typedef long long ll;ll dp[10][101][10][10][10];// 数位,偶数和奇数的差,对差影响的正值,对差影响的负值,长度.int bit[30];int a,b;int dfs(int pos,int sum,int zheng ,int fu,int flag,int bt){    if(pos == 0)        return bt%2 == 0 && sum != 0 && ( sum*(sum+zheng) <= 0 || sum*(sum-fu) <= 0);    if(flag && dp[pos][sum+50][zheng][fu][bt] != -1)    return dp[pos][sum+50][zheng][fu][bt];    int up = flag ? 9 : bit[pos];    ll ans = 0;    for(int i = 0;i <= up;++i)    {        if(bt == 0)        {//前导0            if(i == 0)                ans += dfs(pos-1,0,0,0,flag || i != up,0);            else                ans += dfs(pos-1,sum-i,i-1,9-i,flag || i != up,1);        }        else        {            if(bt&1)                ans += dfs(pos-1,sum+i,max(zheng,9-i),max(fu,i),flag || i != up,bt+1);            else                ans += dfs(pos-1,sum-i,max(zheng,i),max(fu,9-i),flag || i != up,bt+1);        }    }    if(flag)        dp[pos][sum+50][zheng][fu][bt] = ans;    return ans;}ll solve(int x){    if(x <= 0) return 0;    int len = 0;    while(x)    {        bit[++len] = x % 10;        x /= 10;    }    return dfs(len,0,0,0,0,0);}int main(){    memset(dp,-1,sizeof dp);    while(~scanf("%d %d",&a,&b))    {        printf("%lld\n",solve(b) - solve(a-1));    }    return 0;}/*1 3351 7810 99*/
原创粉丝点击