P1874 快速求和(DFS)
阿新 • • 發佈:2018-12-11
題目描述
給定一個數字字串,用最小次數的加法讓字串等於一個給定的目標數字。每次加法就是在字串的某個位置插入一個加號。在裡面要的所有加號都插入後,就像做普通加法那樣來求值。
例如,考慮字串“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; }