题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1226
思路分析:题目要求寻找一串长度不大于500的C进制的密码,且该密码需要为十进制数N的整数倍。
<1>搜索方式选择:由于密码的长度未知(题目限制最大为500),所以状态树的深度比较深,采用dfs搜索效率比较低,选择bfs搜索更好。
<2>剪枝方法:题目中对于每个状态需要采用模运算判断是否为N的整数倍;
由模运算的性质:设存在两个整数a和b,如果a%n==b%n,那么(a*x+c)%n==(b*x+c)%n;
所以如果在搜索的过程出现了某个状态取模的余数与前面某个状态取模的余数相同的情况,那么这个状态被剪枝,不需要延伸下去;
<3>注意事项:对于判断某个数是否为N的倍数的方法中,因为密码的十进制数可能太大导致无法存储,需要借助模运算的性质:
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = ((a % p) * (b % p)) % p
(a ^ b) % p = ((a % p) ^ b) % p
代码如下:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int MAX_N = 500 + 10; const int MAX_M = 5000 + 50; struct Node { int num[MAX_N]; int len; }; bool digit[22]; bool mark[MAX_M]; int N, C, M; int Judge(Node &p) { int len = p.len; int temp = 0; for (int i = 0; i <= len; ++i) temp = (temp * C + p.num[i]) % N; return temp; } bool Bfs() { Node p; queue<Node>Q; p.len = 0; Q.push(p); while (!Q.empty()) { p = Q.front(); Q.pop(); for (int i = (p.len == 0 ? 1 : 0); i < 16; ++i) { if (digit[i]) { int mod = 0; Node q = p; q.num[q.len] = i; mod = Judge(q); if (mod && !mark[mod] && q.len + 1 <= 500) { mark[mod] = true; q.len += 1; Q.push(q); } else if(mod == 0) { for (int i = 0; i <= q.len; ++i) printf("%X", q.num[i]); printf("\n"); return true; } } } } return false; } int main() { int case_times; scanf("%d", &case_times); while (case_times--) { int temp_value; scanf("%d%d%d", &N, &C, &M); memset(digit, false, sizeof(digit)); memset(mark, false, sizeof(mark)); for (int i = 0; i < M; ++i) { scanf("%x", &temp_value); digit[temp_value] = true; } if (N == 0 && digit[0]) printf("0\n"); else if (N == 0 && !digit[0]) printf("give me the bomb please\n"); else if (!Bfs()) printf("give me the bomb please\n"); } return 0; }
时间: 2024-10-06 21:42:42