题目大意
给定一个数字字符串S( 1 ≤ S 的长度 ≤ 40 ),用最少次数的加法让S等于一个给定的目标数字N( 0 ≤ N ≤ 1000 )。每次加法就是在字符串的某个位置插入一个加号。输出最少要添加多少个加号,如果无法让S等于N,则输出-1。
解题思路
对于字符串中相邻的两个数只有两种操作,添加加号或者不添加,我们可以用暴搜在字符串中插入加号,如果最后结果等于N并且小于之前方案的加号数量,那么就刷新结果。
样例
输入
2222 8
输出
3
无剪枝代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstring>
using namespace std;
string s;
int k,n,a[100],ans=1001;
void dfs(int sum,int num,int x,int dep){//sum为当前定结果,num为当前数字,x为当前插入了多少个加号,dep为当前的深度
if(dep>n){
if(sum+num==k)
ans=min(x,ans);
return;
}
dfs(sum,num*10+a[dep],x,dep+1);//不插入加号
dfs(sum+num,a[dep],x+1,dep+1);
return;
}
int main(){
cin>>s;
cin>>k;
n=s.size();
for(int i=1;i<=n;i++)
a[i]=s[i-1]-'0';//将s里的数字存入a数组
dfs(0,a[1],0,2);
if(ans!=1001)
cout<<ans;
else
cout<<-1;
return 0;
}
然后就愉快的超时了。
优化
这时候就要用到剪枝,很容易想到的剪枝有两种,第一种是如果当前插入的加号数量已经大于或等于了之前的最优解的数量,那么无论如何都不可能得出比之前更优的解,那么就返回。第二种则是如果当前的结果或数字已经大于了N,那么就算之后的数再小,也不可能得到N,返回。
正确代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
string s;
int k,n,a[100],ans=1001;
void dfs(int sum,int num,int x,int dep){
if(sum>k||num>k) return;
if(x>=ans) return;
if(dep>n){
if(sum+num==k)
ans=x;
return;
}
dfs(sum,num*10+a[dep],x,dep+1);
dfs(sum+num,a[dep],x+1,dep+1);
return;
}
int main(){
cin>>s;
cin>>k;
n=s.size();
for(int i=1;i<=n;i++)
a[i]=s[i-1]-'0';
dfs(0,a[1],0,2);
if(ans!=1001)
cout<<ans;
else
cout<<-1;
return 0;
}