题意

将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\n", cal(n));
}
return 0;
}
[/cpp]

SPOJ KPSUM ★(数位DP)的更多相关文章

  1. CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)

    题意 求[X,Y]区间内能被其各位数(除0)均整除的数的个数. CF 55D 有些时候因为问题的一些"整体性"而导致在按位统计的过程中不能顺便计算出某些量,所以只能在枚举到最后一位 ...

  2. SPOJ MYQ10 (数位DP)

    题意 询问区间[a,b]中的Mirror Number的个数,其中Mirror Number是指把它横着翻转后还能表示同样的数字. 思路 注意这个可不是回文数..除了0,1,8,别的数字翻转过后就不是 ...

  3. SPOJ - BALNUM 数位dp

    题意:求某一区间内的平衡数个数(指一个数,其中出现过的数,如果是偶数,那么必须出现奇数次,反之偶数次) 题解:用三进制来枚举(0到9)所有情况,0代表没有出现,1代表出现奇数次,2代表出现偶数次dp[ ...

  4. 数位DP:SPOJ KPSUM - The Sum

    KPSUM - The Sum One of your friends wrote numbers 1, 2, 3, ..., N on the sheet of paper. After that ...

  5. 【SPOJ 2319】 BIGSEQ - Sequence (数位DP+高精度)

    BIGSEQ - Sequence You are given the sequence of all K-digit binary numbers: 0, 1,..., 2K-1. You need ...

  6. 【SPOJ 1182】 SORTBIT - Sorted bit squence (数位DP)

    SORTBIT - Sorted bit squence no tags Let's consider the 32 bit representation of all integers i from ...

  7. SPOJ BALNUM - Balanced Numbers - [数位DP][状态压缩]

    题目链接:http://www.spoj.com/problems/BALNUM/en/ Time limit: 0.123s Source limit: 50000B Memory limit: 1 ...

  8. SPOJ BALNUM Balanced Numbers (数位dp)

    题目:http://www.spoj.com/problems/BALNUM/en/ 题意:找出区间[A, B]内所有奇数字出现次数为偶数,偶数字出现次数为计数的数的个数. 分析: 明显的数位dp题, ...

  9. [数位dp] spoj 10738 Ra-One Numbers

    题意:给定x.y.为[x,y]之间有多少个数的偶数位和减去奇数位和等于一. 个位是第一位. 样例: 10=1-0=1 所以10是这种数 思路:数位dp[i][sum][ok] i位和为sum 是否含有 ...

随机推荐

  1. Selenium-Grid2 配置RemoteWebDriver

    为什么要使用Selenium Grid ? 分布式运行大规模的Test 能够通过一个中央点,很容易的运行不同OS上的不同browser 最小化对Grid的维护时间,并能充分利用虚拟设备 Seleniu ...

  2. 一个App项目设计开发完整流程

    作为一个PHP程序猿想转行APP开发可不是件容易的事情,话说隔行如隔山,这隔着一层语言也是多东西需要学习啊,一直对APP开发很感兴趣,最近请教了几个做移动开发的朋友,看了很多的资料,决定把自己学到的东 ...

  3. Gym 100548F Color 2014-2015 ACM-ICPC, Asia Xian Regional Contest (容斥原理+大数取模)

    题意:有N朵花,在M种颜色中选择恰好k种不同的颜色,将这N朵花染色,要求相邻的两朵花颜色不相同. 分析:若限制改为选择不超过k种颜色将N朵花朵染色,则方案数\(f(N,k) = k*(k-1)^{N- ...

  4. 字符编码的发展(ASCII、Unicode、utf-8)

    最近一直在看廖雪峰老师的python网上教程,python内容简单易理解,就没整理,但是字符串编码作为一直困扰自己的问题,看了几遍文章,最终还是将其整理如下,本篇博客总结自廖雪峰老师的网上教程:htt ...

  5. netty9---使用编码解码器

    客户端: package com.client; import java.net.InetSocketAddress; import java.util.Scanner; import java.ut ...

  6. NOIP2019前的训练记录

    \(April\):肛多项式,学\(FWT\)一个小时无果后背了六个公式,看来证明又得咕很久了

  7. GridView自定义自增长的 序号 列

    如图所示,添加一个普通列(非模板列),将其显示文本为    序号 在GridView的RowDataBound事件中作如下处理 后台.CS 代码:

  8. [Linux 002]——Linux的常用命令

    经过前面的学习,大概了解了计算机组成原理和操作系统的一些知识.尽管这些知识都是琐碎的,拼凑的,在以后的工作和学习中仍需进行深入的了解.但是这些预备知识对于准备跨入 Linux 大门的童鞋来说,应该是足 ...

  9. 20145329《Java程序设计》第六周学习总结

    教材学习内容总结 第十章 InputSream与OutputStream Java中,输入串流代表对象为java.io.InputStream实例,输出串流代表对象为java.io.OutputStr ...

  10. 20145333《Java程序设计》课程总结

    每周读书笔记链接汇总 第一周学习总结 第二周学习总结 第三周学习总结 第四周学习总结 第五周学习总结 第六周学习总结 第七周学习总结 第八周学习总结 第九周学习总结 第十周学习总结 实验报告链接汇总 ...