题目链接:Digit Puzzle
题目描述:
给定一个 A ∗ B = C A*B=C A∗B=C的公式,其中有些位置是星号表示可以是任意数字问你需要修改多少位才能让该公式的解唯一确定,如果有多种答案优先输出修改次数少的,如果依然有多种输出字典序最小的解,需要注意的是修改可以把数字改为星号,星号改为数字,但是不能减少或增加每个数字的位数,同时数字不能含有前导零。
例如输入:
∗ ∗ ∗ ∗ ∗ ∗ ∗ **\ \ **\ \ *** ∗∗ ∗∗ ∗∗∗
输出应该为:
$ ∗ ∗ ∗ ∗ 1 ∗ 1 **\ \ **\ \ 1*1 ∗∗ ∗∗ 1∗1
这是因为上述的输出只有 11 ∗ 11 = 121 11*11=121 11∗11=121一种解。具体的输出格式需要参看原题目。
题解:
本题我们直接进行搜索即可,只是需要注意一些剪枝的地方。
我们需要使用 I D ID ID(迭代加深搜)我们只需要依次搜索每个位置的可能值,在填入值的时候需要注意先后顺序应该先填入星号,然后数字从小到大这样能够保证字典序最小,一个剪枝:如果还需要修改的个数大于剩余的位置个数那么可以直接进行剪枝。
在搜索到答案之后,我们还需要判断当前答案是否有唯一的解,判断的过程我们同样需要依次填入数字,不过需要注意的是,在判断时候我们实际上只需要把前两个数字的空白都填完就可以进行判断了这样可以推断出第三个数字应该为多少,而如果将三个数字都填完在进行判断的话由于第三个数最多有四位,每个位置有十个数字可以填入,那么复杂度就可能是原来的 10000 10000 10000倍,这样就会超时。
代码:
#include <bits/stdc++.h>
const int MAXN = 10;
using namespace std;
char a[MAXN], b[MAXN], c[MAXN];
int lenA, lenB, lenC, caseID, cnt, maxDepth;
bool getInput()
{
cin >> a;
if (a[0] == '0') { return false; }
cin >> b >> c;
lenA = strlen(a);
lenB = strlen(b);
lenC = strlen(c);
return true;
}
void getCnt(int pa, int pb);
void fillNumber(char *a, int startPos, int len, int npa, int npb)
{
if (isdigit(a[startPos])) { getCnt(npa, npb); }
else {
for (int number = 0; number <= 9; number++) {
if (startPos == 0 && len != 1 && number == 0) { continue; }
a[startPos] = number + '0';
getCnt(npa, npb);
}
a[startPos] = '*';
}
}
void getCnt(int pa, int pb) {
if (cnt > 1) { return; }
if (a[lenA - 1] != '*' && b[lenB - 1] != '*' && c[lenC - 1] != '*'
&& (a[lenA - 1] - '0') * (b[lenB - 1] - '0') % 10 != c[lenC - 1] - '0') { return; }
if (pb == lenB) {
int numberA = atoi(a);
int numberB = atoi(b);
int numberC = numberA * numberB;
for (int i = lenC - 1; i >= 0; i--) {
if (i == 0 && numberC == 0 && lenC > 1) { return; }
if (c[i] != '*' && numberC % 10 != c[i] - '0') { return; }
numberC /= 10;
}
if (numberC != 0) { return; }
cnt++;
return;
}
if (pa != lenA) { fillNumber(a, pa, lenA, pa + 1, pb); }
else if (pb != lenB) { fillNumber(b, pb, lenB, pa, pb + 1);}
}
bool check()
{
cnt = 0;
getCnt(0, 0);
return cnt == 1;
}
bool dfs(int nowDepth, int pa, int pb, int pc);
bool change(char *a, int startPos, int len, int nowDepth, int npa, int npb, int npc)
{
char temp = a[startPos];
if (a[startPos] != '*') { // 尝试改为星号,星号的ANSCII码小,所以需要先搜索改成星号的情况
a[startPos] = '*';
if (dfs(nowDepth + 1, npa, npb, npc)) { return true; }
a[startPos] = temp;
} else {
if (dfs(nowDepth, npa, npb, npc)) { return true; }
}
for (int number = 0; number <= 9; number++) { // 尝试改为数字
if (len > 1 && startPos == 0 && number == 0) { continue; } // 不能含有前导零
a[startPos] = number + '0';
if (temp - '0' == number) {
if (dfs(nowDepth, npa, npb, npc)) { return true; }
continue;
}
if (dfs(nowDepth + 1, npa, npb, npc)) { return true; }
}
a[startPos] = temp;
return false;
}
bool dfs(int nowDepth, int pa, int pb, int pc)
{
if (nowDepth == maxDepth) { return check(); }
// 剩余能够修改的位置少于还需要进行的修改次数则直接进行剪枝
if (maxDepth - nowDepth > lenA - pa + lenB - pb + lenC - pc) { return false; }
if (pa != lenA) {
if (change(a, pa, lenA, nowDepth, pa + 1, pb, pc)) { return true; }
} else if (pb != lenB) {
if (change(b, pb, lenB, nowDepth, pa, pb + 1, pc)) { return true; }
} else {
if (change(c, pc, lenC, nowDepth, pa, pb, pc + 1)) { return true; }
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
while (getInput()) {
for (maxDepth = 0; ; maxDepth++) {
if (dfs(0, 0, 0, 0)) {
caseID++;
cout << "Case " << caseID << ": ";
cout << a << " " << b << " " << c << endl;
break;
}
}
}
return 0;
}