题目链接:Click here~~
题意:
找一个 n 的最小倍数 x ,使 x 中恰好包含 m 个不同的数字。(n <= 10^3 , m <= 10)
解题思路:
首先,要理解题意中”恰好 m 个不同“不一定是 m 个数字。
今天有人在群里问的这道题,想了想,果然不会做。。。搜完题解,差不多懂了,记录下。解法叫做 BFS。
为什么可以 BFS 呢?
直观的想法肯定是, 从小到大枚举 x,每次判断 x 是否满足要求,但明显没有枚举的上界。所以复杂度是无穷大的。
所以要换个思路想,能不能找到一个更加合理、更能使用到题中条件的状态?
做法是 :总状态大小是 state[1<<m][n] ,对于 state[i][j] ,i 表示选取了哪些数字,j 表示当前这些数字的余数。
目标状态就是,选取了恰好 m 个不同数字,且余数为 0 。
之所以可以这样,因为对问题有影响的因素只有两个:已经选取了哪些数字 和 当前的余数。
枚举的时候,可以按位来枚举,先枚举第一位,再枚举第二位。这样当前余数也就好求了 。
为了简化代码,可以把状态搞成一维的。另外每个状态需要存 4 个值。
复杂度 O(1<<10 * 1000 * 10)。
跑了9000+MS,时限太科学了。。。
#include <queue>
#include <string>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct State
{
bool vis;
char num;
int pre,cnt;
}s[(1<<10)*1005];
int bfs(int n,int m)
{
queue<int> Q;
Q.push(0);
while(!Q.empty())
{
int cur = Q.front();
Q.pop();
for(int i=0;i<10;i++)
{
if(i == 0 && cur == 0)
continue;
int remaind = (cur % 1000 * 10 + i) % n ;
int mask = (cur / 1000 | (1<<i)) * 1000 + remaind;
if(s[mask].vis)
continue;
s[mask].vis = true;
s[mask].pre = cur;
s[mask].num = '0' + i;
s[mask].cnt = s[cur].cnt + (cur / 1000 & (1<<i) ? 0 : 1);
if(s[mask].cnt == m && remaind == 0)
{
while(!Q.empty())
Q.pop();
return mask;
}
if(s[mask].cnt <= m)
Q.push(mask);
}
}
return -1;
}
string quotient(int n,const string& ans)
{
string res;
int cur = 0 , i = 0;
while(cur < n)
cur = cur * 10 + ans[i++] - '0';
res += cur / n + '0';
while(i < (int)ans.size())
{
cur = cur % n * 10 + ans[i++] - '0';
res += cur / n + '0';
}
return res;
}
void print(int n,int state)
{
string ans;
while(state != 0)
{
ans += s[state].num;
state = s[state].pre;
}
reverse(ans.begin(),ans.end());
printf("%s=%d*%s\n",ans.c_str(),n,quotient(n,ans).c_str());
}
int main()
{
int n,m;
scanf("%*d");
while(~scanf("%d%d",&n,&m))
{
memset(s,0,sizeof(s));
int ans = bfs(n,m);
if(ans < 0)
puts("Impossible");
else
print(n,ans);
}
return 0;
}