打表+暴搜
这道题目,显然是需要打表的,不过打表的方式可以有很多。
我是打了两个表,分别表示每个数字所需的火柴棒根数以及从一个数字到另一个数字,除了需要去除或加入的火柴棒外,至少需要几根火柴棒。
然后我们就可以暴搜了,大体就是枚举等式左边两个数每一位的值,并枚举中间的运算符是\(+\)还是\(-\),然后计算出等式右边的值,判断是否合法。
中间过程可以加上一些剪枝。
注意当火柴棒从某一位移到另一位时,我们可以规定,去除火柴棒需要算步数,加入火柴棒则无需算步数,这样就可以避免重复了。
具体实现有一些小细节,可以参考代码
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
const int q[10]={6,2,5,5,4,5,6,3,7,6};//打表
const int p[10][10]=//打表
{
{0,0,1,1,1,1,1,0,0,1},{0,0,1,0,0,1,1,0,0,0},{1,1,0,1,2,2,1,1,0,1},{1,0,1,0,1,1,1,0,0,0},{1,0,2,1,0,1,1,1,0,0},
{1,1,2,1,1,0,0,1,0,0},{1,1,1,1,1,0,0,1,0,1},{0,0,1,0,1,1,1,0,0,0},{0,0,0,0,0,0,0,0,0,0},{1,0,1,0,0,0,1,0,0,0}
};
int n,a[N+5],b[N+5];string s;
class Dfser//暴搜
{
private:
int ans;
I void Calc(CI x,CI y,RI v,CI w)//计算,判断答案是否合法
{
if(w<0) return;RI i,t,k=0,t1=0,t2=0;for(i=x;i^n;++i) t1+=b[i];
t=w;W(t2+=q[t%10],++k,t/=10);if(k>n-x||(t1+y)^t2) return;
for(t=w,i=n-1;i>=x;--i)
{
if(i^(n-1)&&!t) return;
v+=p[a[i]][t%10]+max(b[i]-q[t%10],0),t/=10;
}Gmin(ans,v);
}
I void dfs(CI x,CI y,CI v,CI sv,CI op,CI tot)//暴搜,枚举每一位的值和运算符
{
if(abs(y)>5*(n-x)||ans<=v) return;if(s[x]=='=') return Calc(x+1,y,v,tot+sv*op);
if(s[x]=='+') dfs(x+1,y,v,0,1,tot+sv*op),dfs(x+1,y+1,v+1,0,-1,tot+sv*op);
else if(s[x]=='-') dfs(x+1,y,v,0,-1,tot+sv*op),dfs(x+1,y-1,v,0,1,tot+sv*op);
else
{
dfs(x+1,y,v,sv*10+a[x],op,tot);
for(RI i=!((x&&isdigit(s[x-1]))||!isdigit(s[x+1]));i<=9;++i)
a[x]^i&&(dfs(x+1,y+b[x]-q[i],v+p[a[x]][i]+max(b[x]-q[i],0),sv*10+i,op,tot),0);
}
}
public:
I void Solve() {ans=1e9,dfs(0,0,0,0,1,0),ans==1e9?puts("-1"):printf("%d",ans);}
}D;
int main()
{
freopen("A.in","r",stdin),freopen("A.out","w",stdout);
cin>>s,n=s.length(),s+="@";for(RI i=0;i^n;++i) b[i]=q[a[i]=s[i]&15];
return D.Solve(),0;
}