1. 程式人生 > >P1874 快速求和(DFS)

P1874 快速求和(DFS)

題目描述

給定一個數字字串,用最小次數的加法讓字串等於一個給定的目標數字。每次加法就是在字串的某個位置插入一個加號。在裡面要的所有加號都插入後,就像做普通加法那樣來求值。

例如,考慮字串“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>
typedef int ll;
using namespace std;
const int INF = 0x3f3f3f3f, maxn = 100001;
ll 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(ll i = 0; i < len; i ++) a[i + 1] = s[i] - '0';
	for(ll 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(ll i = 1; i <= len; i++)
		for(ll k = 1; k <= len && k <= 6; k++)
			if(k <= i)                               //k < i
				for(ll 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;
}