题目描述
给定一个数字字符串,用最小次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在里面要的所有加号都插入后,就像做普通加法那样来求值。
例如,考虑字符串“12”,做0次加法,我们得到数字12。如果插入1个加号,我们得到3,因此,这个例子中,最少用1次加法就得到数字3.
再举一例,考虑字符串“303”和目标数字6,最佳方法不是“3+0+3”。而是“3+03”。能这样做是因为1个数的前导0不会改变它的大小。
写一个程序来实现这个算法。
输入输出格式
输入格式:
第1行:1个字符串s(1<=s的长度<=40);
第2行:1个整数N(N<=100000)。
输出格式:
第1行:1个整数K,表示最少的加法次数让S等N。如果怎么做都不能让S等于N,则输出-1
输入输出样例
输入样例#1: 复制
99999
45
输出样例#1: 复制
4
题解:
一开始想着暴力解决,却发现代码既然不会写,然后想到dfs
先给出我一开始开始的dfs代码:
#include <iostream>
using namespace std;
string s;
int n, ans = 1000, maxplus;
int a[50];
bool flag = 0;
int turn(int start, int end) { //从 a + b 的值记录 返回
int sum = 0;
for(int i = start; i <= end; ++i)
sum *= 10,sum += a[i];
return sum;
}
void dfs(int now, int step, int cntplus) { //当前值 当前加号位置 当前加号数量
if(cntplus > maxplus) return; //找到了就停止, 加号大于最大数量也停止
if(now == n) { // 找到全部停止
flag = 1;
ans = min(ans,cntplus);
return;
}
for(int i = step + 1; i < s.size(); ++i) //往后找
dfs(now + turn(step + 1, i),i,cntplus + 1);
}
int main() {
cin >> s >> n;
for(int i = 0; i < s.size(); i++) {
a[i] = s[i] - '0';
}
maxplus = s.size() - 1; //最大的加号数量
for(int i = 0; i < s.size() - 1; ++i) dfs(turn(0,i), i, 0); //第一个加号 坑人还有特殊情况 00000 0
if(flag ) cout << ans ;
else cout << -1 ;
return 0;
}
测试用例有三个没有通过很难受。
下面是dp的代码: 特殊情况 搞得要死人如下两组
0000 0 222 0
// 前 i 位数字 组成 j 的最少加法次数 f[i][j] = min { f[i-k][j-s[i-k+1],f[i][j]}
解释代码里很详细了。
#include <iostream>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f, maxn = 100001;
int n, a[41], dp[42][maxn], sum[42][42] = {0};// 前 i 位数字 组成 j 的最少加法次数 f[i][j] = min { f[i-k][j-s[i-k+1]
string s;
int main() {
cin >> s;
int len = s.size();
for(int i = 0; i < len; i ++) a[i + 1] = s[i] - '0';
for(int i = 1; i <= len; i++) {
sum[i][i] = a[i];
for(int j =i + 1; j <= len; j++)
sum[i][j] += sum[i][j - 1] * 10 + a[j]; //sum表示从i 到 j 的值
}
cin >> n;
if(!n&&!sum[1][len]) return cout << 0,0;
memset(dp, 0x3f, sizeof(dp)),dp[0][0] = 0;
for(int i = 1; i <= len; i++)
for(int k = 1; k <= len && k <= 6; k++)
if(k <= i) //k < i
for(int j = sum[i - k +1][i]; j <= n; j++)
dp[i][j] = min(dp[i-k][j-sum[i-k+1][i]] + 1,dp[i][j]); //dp 到 i 加到 j 的 最小步数
if(dp[len][n] == INF) cout << -1 ;
else cout << dp[len][n] - 1;
return 0;
}