问题描述
http://www.nowcoder.com/test/question/done?tid=4107357&qid=45843#summary
小易总是感觉饥饿,所以作为章鱼的小易经常出去寻找贝壳吃。最开始小易在一个初始位置x_0。对于小易所处的当前位置x,他只能通过神秘的力量移动到 4 * x + 3或者8 * x + 7。因为使用神秘力量要耗费太多体力,所以它只能使用神秘力量最多100,000次。贝壳总生长在能被1,000,000,007整除的位置(比如:位置0,位置1,000,000,007,位置2,000,000,014等)。小易需要你帮忙计算最少需要使用多少次神秘力量就能吃到贝壳。
笔记
参考了牛客网大神的解答,代码部分是参考大神的代码写的。
题目故意把数字设置的那么大,因此类型要用long long才行。
使用了一个队列q来保存从x_0开始访问过的节点,使用一个map来记录访问这个节点所需的次数。因此是广度优先遍历。
这里还用到了取模的运算规则。
这里是用到的是
如果a%c=a
,那么
(a*x+b)%c = (a*(x%c)+b)%c
这个公式说明了,对一个数x,先做a*x+b然后再取模,等于先对x取模,然后做(a*(x%c)+b)
,最后再取模。
这个有什么用呢?由于题目中给出的数字很大,如果一直做a*x+b的递推,有可能会超过计算机所能表达的数字,但是如果有这个规律,每次都使用取模后的结果做递推,然后再取模,那么递推的结果永远都不会超过模的大小。
具体的证明过程请看:
代码
#include <iostream>
#include <queue>
#include <map>
using namespace std;
typedef long long llint;
const llint base = 1000000007LL;
int solve(int x0)
{
queue<int> q;//存储模
map<int, int> m;//存储{模: 数量}
q.push(x0);
m[x0] = 0;
while (!q.empty())
{
llint now = q.front();
q.pop();
if (now == 0)
return m[now];
if (m[now] > 100000)
continue;//不计算下一个数了
int next0 = (now * 4 + 3) % base;
if (!m[next0])
{
m[next0] = m[now] + 1;
q.push(next0);
}
int next1 = (now * 8 + 7) % base;
if (!m[next1])
{
m[next1] = m[now] + 1;
q.push(next1);
}
}
return -1;
}
int main()
{
int x0;
while (cin >> x0)
{
cout << solve(x0) << endl;
}
return 0;
}