我在代码部队(http://codeforces.com/problemset/problem/1070/A)上发现了此问题,并且我试图了解发布的一种非常优雅的解决方案:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int d,s;
struct node
{
int mod,sum;
char s[700];
int len;
};
queue<node> Q;
bool v[512][5200];
int main()
{
scanf("%d%d",&d,&s);
Q.push({0,0,0,0});
while(!Q.empty())
{
node a=Q.front();Q.pop();
for(int i=0;i<10;i++)
{
node b=a;
b.s[b.len++]=i+'0';
b.mod=(b.mod*10+i)%d;
b.sum+=i;
if(v[b.mod][b.sum] || b.sum>s) continue;
v[b.mod][b.sum]=1;
if(b.mod==0&&b.sum==s)
{
puts(b.s);
return 0;
}
Q.push(b);
}
}
puts("-1");
return 0;
}
我知道通过将数字添加到前缀并将它们放在队列中来完成树状搜索。搜索是这样的1,2,3,4 ...然后10,11,12 ... 20,21,22 ...等等。
我不了解以下停止条件:
if(v[b.mod][b.sum] || b.sum>s) continue;
很明显,如果数字的总和大于s,则当前路径不值得追求。但是,如果我们之前遇到一个数字,其余数和数字总和相同,那么丢弃路径的依据是什么?
一个例子是这样的: d = 13 s = 50
路径到达120时,它将触发条件,因为我们已经看到了数字3,该数字与120具有相同的余数,并且具有相同的数字总和。
答案 0 :(得分:1)
使用示例120和3以及一些modular arithmetic,这很容易说明,为什么基于已经测试过3的事实来整理出120:
模块化算术中的加法和乘法定义为:
((a mod n) + (b mod n)) mod n = (a + b) mod n
((a mod n) * (b mod n)) mod n = (a * b) mod n
使用这些,我们可以证明,对于任何其他数字x
,余数d
都将保持不变:
(120 * 10 + x) mod d = ((120 mod d) * (10 mod d) + (x mod d)) mod d
( 3 * 10 + x) mod d = (( 3 mod d) * (10 mod d) + (x mod d)) mod d
因为我们知道3 mod d = 120 mod d
,所以我们知道,如果我们用相同的附加数字测试它们,则上述两个术语将具有相同的余数。
但是它们的数字总和也相等,这意味着可以将相同的一组新数字应用于两个数字。因此,就问题而言,120和3是等效的,前者可以丢弃。