• SPOJ KPSUM ★(数位DP)


    题意

    将1~N(1<=N<=10^15)写在纸上,然后在相邻的数字间交替插入+和-,求最后的结果。例如当N为12时,答案为:+1-2+3-4+5-6+7-8+9-1+0-1+1-1+2=5。

    思路

    花了一上午时间调BUG……必须承认SPOJ上的好题很多~每次做都有很大收获…… 我发现原来记忆化搜索的数位DP不止可以做统计,还可以做计算我们应该把记忆化搜索形式的数位DP理解成一种处理与各位数字有关问题的一种方法,或者也可以延伸到字符串上。 我们可以把数列在纸上写一下:
    -0
    +1
    -2
    +3
    -4
    +5
    ……
    -8
    +9
    
    -1+0
    -1+1
    ……
    -1+9
    -2+0
    -2+1
    ……
    -9+9
    
    -1+0-0
    +1-0+1
    -1+0-2
    ……
    +1-9+9
    -2+0-0
    …………
    可以发现:如果数位为偶数,那么对应位的符号都一样,并且第一个符号为负,交替改变;如果数位为奇数,那么每一位符号都是交错的,可以两两抵消,只需计算个位即可。 然后状态设计就好说了:pos、limit这些基本参数不用说。还需要一个pre记录上一位,以便数位为奇数时计算最后一位;一个sum表示各位数符号交替之和,用于数位为偶数时的计算;一个sub表示当前数位的符号(+/-);一个state表示数位是奇数还是偶数。 这里就需要注意最重要的一点设计dp[][]状态时一定要使得每一个dp数组都唯一且明确对应一个区间,这样才可以只在程序开始时初始化dp数组,否则就需要在每次询问[1,N]区间前都初始化!什么意思?比如一开始时,我们会把N拆成数位存起来,而我的state表示的是当前枚举的数从左数第一个非零位的位置(即数的起始位置),以此来判断最后数位的奇偶性。那这样的state就很不明确了,因为他的意义会随着N的数位的变动而变化,我们当然可以增加一维数组表示当前N的位数,但那样就绕远了,而且空间不允许。所以我修正了state的含义:state=0表示当前奇偶不确定,即前面枚举的数位全都是0;state=1表示当前数位为奇数;state=2表示偶数。这个在第一次i!=0时就可以更新状态,详细看代码吧~

    代码

      [cpp] #include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <string> #include <cstring> #include <vector> #include <set> #include <stack> #include <queue> #define MID(x,y) ((x+y)/2) #define MEM(a,b) memset(a,b,sizeof(a)) #define REP(i, begin, m) for (int i = begin; i < begin+m; i ++) using namespace std; typedef long long LL; typedef vector VI; typedef set SETI; typedef queue QI; typedef stack SI; const int oo = 0x3fffffff; VI num; LL dp[16][10][280][3][2]; LL dfs(int pos, int pre, int sum, int state, bool sub, bool limit){ if (pos == -1){ if (state == 1) return (pre&1)?pre:-pre; else return sum; } if (!limit && ~dp[pos][pre][sum+140][state][sub]) return dp[pos][pre][sum+140][state][sub]; int end = limit?num[pos]:9; LL res = 0; for (int i = 0; i <= end; i ++){ bool st = !state && (i == 0); int next_state = state; if (!state && i > 0){ if ((pos+1)%2==0) next_state = 2; else next_state = 1; } res += dfs(pos-1, i, sub?sum-i:sum+i, next_state, st?true:sub^1, limit&&(i==end)); } return limit?res:dp[pos][pre][sum+140][state][sub]=res; } LL cal(LL x){ num.clear(); LL res = 0; if (x % 2 == 0){ LL tmp = x; while(tmp){ num.push_back(tmp%10); tmp /= 10; } for (int i = (int)num.size()-1, k = -1; i >= 0; i --, k*=-1){ res += num[i]*k; } x --; } num.clear(); while(x){ num.push_back(x%10); x /= 10; } int len = (int)num.size(); return res + dfs(len-1, 0, 0, 0, true, true); } int main(){ LL n; MEM(dp, -1); while(~scanf("%lld", &n)){ if (n == 0) break; printf("%lld ", cal(n)); } return 0; } [/cpp]
  • 相关阅读:
    Hdu 5396 Expression (区间Dp)
    Lightoj 1174
    codeforces 570 D. Tree Requests (dfs)
    codeforces 570 E. Pig and Palindromes (DP)
    Hdu 5385 The path
    Hdu 5384 Danganronpa (AC自动机模板)
    Hdu 5372 Segment Game (树状数组)
    Hdu 5379 Mahjong tree (dfs + 组合数)
    Hdu 5371 Hotaru's problem (manacher+枚举)
    Face The Right Way---hdu3276(开关问题)
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114101.html
Copyright © 2020-2023  润新知