P1874 快速求和(DFS&&DP)

题目描述

给定一个数字字符串,用最小次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在里面要的所有加号都插入后,就像做普通加法那样来求值。

例如,考虑字符串“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)。

输出格式:

扫描二维码关注公众号,回复: 3324464 查看本文章

第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;
}

猜你喜欢

转载自blog.csdn.net/FireflyNo1/article/details/82811437