经验谈
- 不要靠猜, 不要靠感觉 直觉 运气, 不要理想当然, 一定要证明 万分的保障;
比如贪心, 不要带有试试的心态 碰运气的心态, 试错的代价是很大的!
试错后, 你还是得去证明, 还要花更多的时间精力 去重新来过
易错点总结
拆分数位
对于一个非负整数a >= 0
, 拆分得到其所有数位
很自然想到
for( auto i = a; i > 0; i /= 10){ bits.push_back( i % 10);}
但是, 当a == 0
时, bits
是空的, 应该把0
也放到bits
里!!!
思路一定要完整全面, 不要追求代码简短效率等, 首先要考虑到所有极限情况;
正确代码是:
vector< int> bits;
for( auto i = a; i > 0; i /= 10){
bits.push_back( i % 10);
}
reverse( bits.begin(), bits.end());
if( bits.empty()){ bits.push_back( 0);}
此时,a = 123
则 bits = {1, 2, 3}
DP的空
状态
以线性DP为例子: 123
dp[123] 由 [3] 和 dp[12] 推出, dp[12] 由 [2] 和 dp[1]推出
… 但是, dp[1] 由 [1] 和 dp[0]推出, 而dp[ 0]是什么呢?
… 假如dp[ 0]
是空 是非法的, 那么, dp[ 1]
也是非法的; 因为, dp[1] 由 [1] + dp[ 0] 推出
, dp[ 0]
非法, 则dp[ 1]
也非法
… 因此, dp[ 0]并不是空, 他其实是代表一个()
(空数, 空值), 这个元素是(空)的, 是非法的, 但是, dp[ 0]不是空的, 他里面有一个元素 就是()
… 此时[1]
可以拆分为 [1] + ()
的形式,
… 换句话说, 假如dp代表其集合里的元素个数; dp[ 0]
必须为1(因为()
空元素是他的唯一元素), 而不能是0
… … 否则, 你的dp[ 1]
一定为0, 因为无法进行拆分, 导致1
是非法的
从元素个数来看, 假如每个位 都有0-9
共10种选择,
在[1]
时, 他有10^1
种形态; 在[2]
时, 他有10^2
种形态; 即在[k]
时, 他有10^k
种形态
那么, 在[0]
时, 他有10^0 = 1
种形态; 那么, 问题来了: 这个形态是什么?
… 这个形态, 是一种不存在的状态, 简称为(空状态)! 但他是存在的, 是一种方案! 只是, 他无形…
这个(不存在的数), 也作为一个(元素), 在这个集合里…
… 这个(空状态), 可以 (和任意状态)组合; 比如: 123 = 123 + ()
, 其中()就表示这个(空状态), 这个(空状态) 是一种方案!
比如在进行数位DP时, 定义Dp[ a]
为: [a个0 -- a个9]
之间的 这10^a
个数 中, 合法数的个数; (即一定会有: Dp[ a] <= 10^a
)
那么, 对于Dp[ 0]
是代表那些数? 这个问题非常重要
… 按照定义看, 他代表有10^0 = 1
个数; (即, 他的集合 不是空的!!)
… 但有一点不同, 这个数, 无法像Dp[ >0]
所代表的那些数, 可以显式的写成数的形式; 这个元素, 是无形的… 他是(空)的
… 因此, 这个元素 一定是非法的; … 因为: (合法) 的前提是: 这个元素 是一个 (数), 就可以显式表达; 而它就无法显式表达
… 即, Dp[ k], k>0
代表 10^k
个状态, 每个状态 就对应一个字符串 (0-9
组成的字符串)
… … 但是, 当Dp[ k], k=0
时, 它代表 1
个状态 (即空状态), 但不代表 (任何字符串), 即空状态 无法对应为 字符串;
… … 而我们的(合法), 判定的是(字符串); 所以, (空状态) 是非法的; 但它确实是个(状态)
… 即Dp[ 0] = 0
, 这是肯定是; 因为(空状态) 是非法的;
… 这个(空状态), 可以与(任意状态)组合; 123 = 123 + ()
, 其中那个()
表示(空状态)
… 但是, 如果涉及(元素个数) 不涉及合法性, 即10^k
, Dp[ 0] = 1
, 表示它有1
个元素, 不可以设置成0
个
如果你把Dp[ a]
定义为: [0 -- (10^a - 1)]
这些数, 这和上面的 Dp
定义, 是不同的!
上面定义的是(字符串数) , 即Dp[ 2] = [00 -- 99]
不是[0 -- 99]
, 但是这里, Dp[ 2] = [0 -- 99]
;
此时, Dp[ 0] = [0 -- 0]
, 这是一个状态, 这个状态同时也是合法的 (是0
, 是个 数字)
但之前的Dp[ 0] = ()
, 是一个状态, 但是不是合法的 (因为他不是个 字符串数)
auto
巧用
void Func( int _a){
vector< int> bits;
for( int i = _a; i > 0; i /= 10){
bits.push_back( i % 10);
}
}
bits
存储: _a
的每个数位, 这个一个很经典的算法;
可是, 如果我们后期改进函数, 将_a
修改为了Ll_
类型 , 此时, int i = _a
就出错了!
就必须再手动修改为: Ll_ i = _a;
非常容易遗忘;
好的办法是: for( auto i = _a; ...)
, 这多好!
size_t
无符号整数溢出
对于容器, 通常都会有一个size()
的函数, 表示容器里元素个数, 其通常是size_t
无符号类型
当容器为空时, 即size() == 0
v.size() - 1
, a
的值会是: 最大的正数(二进制全1)
当使用for( int i = 0; i <= v.size() - 1; ++i)
来遍历元素, 这会导致死循环!
比如:
#define FOR( i, l, r) for( int i = l; i <= r; ++i)
FOR( i, 0, v.size() - 1){}
这会死循环的
解决办法:
for( int i = 0; i < v.size(); ++i)
, 左闭右开方式for( int i = 0; i <= int( v.size()) - 1; ++i)
, 转换为int类型