一个长度为n的整数数组A,在每两个数之间填入 ‘+’ 或者 ‘-’, 使得最终运算结果尽可能接近给定的评估值 k. 共有n-1个运算符,运算符为’+‘或者’-’. 可以用oper数组将途径的运算符进行标记,递归结束时计算sum值。复杂度 O ( 2 n ) O(2^n) O(2n)
# include <iostream> # include <cstdio> # include <cmath> int n, k; int a[25]; bool oper[25]; int val; void dfs(int step) { if (step >= n) { int sum = a[1]; for (int i = 1; i < n; i++) { if (!oper[i]) { sum += a[i+1]; } else { sum -= a[i+1]; } } val = std::min(val, abs(sum - k)); return; } oper[step] = 0; dfs(step + 1); oper[step] = 1; dfs(step + 1); } int main() { int T; std::cin >> T; while (T--) { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } val = 0x3f3f3f3f; dfs(1); printf("%d\n", val); } return 0; }也可以将sum作为参数直接进行递归。此方法耗时更少。
# include <iostream> # include <cstdio> # include <cmath> int n, k; int a[25]; int val; void dfs(int step, int sum) { if (step >= n + 1) { val = std::min(val, abs(sum - k)); return; } dfs(step + 1, sum + a[step]); dfs(step + 1, sum - a[step]); return; } int main() { int T; std::cin >> T; while (T--) { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } val = 0x3f3f3f3f; dfs(2, a[1]); printf("%d\n", val); } return 0; }总结:常规的DFS处理数据可以选择用1个参数且保存路径,在递归结束时根据路径求出结果,也可以将所需要求的结果作为另一个参数不保存路径。
一个长度为n的整数数组a,判断是否可以从中选出若干数,使得它们的和恰好为k。
# include <iostream> # include <cstdio> int n; int k; int a[25]; bool vis[25]; bool dfs(int step, int sum) { if (sum > k) { return false; } if (step >= n) { return sum == k; } vis[step] = false; if (dfs(step + 1, sum)) { return true; } vis[step] = true; if (dfs(step + 1, sum + a[step])) { return true; } return false; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } scanf("%d", &k); if (dfs(0, 0)) { printf("Yes\n"); for (int i = 0; i < n; i++) { if (vis[i]) printf("%d ", a[i]); } } else { printf("No\n"); } return 0; }一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序, 自己手里能拿到的初始牌型组合一共有多少种?
方法一:sum代表目前手中的卡牌张数,step代表目前手中的卡牌点数,cnt代表当前卡牌点数在手中的数量,卡牌点数大于13时剪掉,当前卡牌点数在手中的数量大于4时剪掉,但不加方案数。
# include <iostream> typedef long long ll; ll ans; int cnt; void dfs(int sum, int step) { if (cnt > 4) { return; } if (sum == 13) { ++ans; return; } if (sum > 13) { return; } for (int i = step; i >= 1; i--) { cnt++; dfs(sum + 1, i); cnt = 0; } } int main() { dfs(0, 13); std::cout << ans; return 0; }方法二:step代表目前手中的卡牌点数,cnt代表目前手中的卡牌张数,当手中有13张卡牌时停止递归,方案数+1。当目前手中卡牌点数超过13时或者手中卡牌数超过13时也剪掉,但不加方案数。
# include <iostream> typedef long long ll; ll ans = 0; void dfs(int step, int cnt) { if (cnt == 13) { ++ans; return; } if (step >= 13 || cnt >= 13) { return; } for (int i = 0; i <= 4; i++) { dfs(step + 1, cnt + i); } } int main() { dfs(0, 0); std::cout << ans; return 0; }运行结果:3598180
如果需要考虑花色呢?那就将卡牌数加上权值,w[0] = 1, w[1] = 4, w[2] = 6, w[3] = 4, w[4] = 1, 在DFS时保存路径,在递归结束时方案数加上该路径的权值的乘积。
将一张100元的钞票换成1元、2元、5元和10元的零钱,每种零钞至少一张,编写程序输出所有的换法。 第一行输出换算的方案数T,接下来T行每行4个数字a,b,c,d分别代表1元的张数、2元的张数、5元的张数、10元的张数。按照字典序从小到大输出。
先将所有的钞票张数减一,sum总和减18,再进行DFS。
其余部分和例3差不多。
# include <iostream> # include <set> # include <tuple> int money[5] = {0, 1, 2, 5, 10}; int arr[107]; int cnt[15]; typedef std::tuple<int, int, int, int> P; std::set<P> st; void dfs(int sum, int choose) { if (sum == 100 - 1 - 2 - 5 - 10) { P t = std::make_tuple(cnt[1] + 1, cnt[2] + 1, cnt[5] + 1, cnt[10] + 1); st.insert(t); return ; } else if (sum > 100 - 1 - 2 - 5 - 10) { return; } else { for (int i = choose; i >= 1; i--) { cnt[money[i]]++; dfs(sum + money[i], i); cnt[money[i]]--; } } } int main() { dfs(0, 4); std::cout << st.size() << "\n"; for (auto it = st.begin(); it != st.end(); it++) { std::cout << std::get<0>(*it) << " "; std::cout << std::get<1>(*it) << " "; std::cout << std::get<2>(*it) << " "; std::cout << std::get<3>(*it) << "\n"; } return 0; } # include <iostream> # include <set> # include <tuple> int money[5] = {1, 2, 5, 10}; int cnt[15]; typedef std::tuple<int, int, int, int> P; std::set<P> st; void dfs(int step, int sum) { if (sum == 100 - 1 - 2 - 5 - 10) { P t = std::make_tuple(cnt[0] + 1, cnt[1] + 1, cnt[2] + 1, cnt[3] + 1); st.insert(t); return; } if (step >= 4) { return; } for (int i = 0; i <= 100; i++) { cnt[step] += i; if (sum + money[step] * i <= 100 - 1 - 2 - 5 - 10) { dfs(step + 1, sum + money[step] * i); } else { cnt[step] -= i; break; } cnt[step] -= i; } } int main() { dfs(0, 0); std::cout << st.size() << "\n"; for (auto it = st.begin(); it != st.end(); it++) { std::cout << std::get<0>(*it) << " "; std::cout << std::get<1>(*it) << " "; std::cout << std::get<2>(*it) << " "; std::cout << std::get<3>(*it) << "\n"; } return 0; }从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。 方法1:二进制状态压缩
/************************** https://www.acwing.com/problem/content/94/ Time 33 ms Memory 860 KB ***************************/ # include <iostream> # include <cstdio> int n; void print(int num) { for (int i = 0; i < n; i++) { if ((num >> i) & 1) { std::cout << i + 1 << " "; } } } int main() { std::cin >> n; for (int i = 0; i < 1 << n; i++) { print(i); puts(""); } return 0; }方法2:DFS
/************************** https://www.acwing.com/problem/content/description/94/ Time 32 ms Memory 852 KB **************************/ # include <iostream> # include <vector> int n; std::vector<int> chosen; void print() { for (int i = 0; i < chosen.size(); i++) { std::cout << chosen[i] << " "; } puts(""); } void dfs(int step) { if (step == n + 1) { print(); return; } dfs(step + 1); chosen.push_back(step); dfs(step + 1); chosen.pop_back(); } int main() { std::cin >> n; dfs(1); return 0; }和递归实现指数型枚举的思路差不多,只不过多了剪枝和排序的步骤。 当chosen数组大小大于m或者接下来的递归即使所有的数全部选上也不能达到m个时终止递归。 打印的数字需要进行排列,可以将chosen数组放入一个vecotor中,dfs完成后先进行排序,再进行输出。由于vector的比较大小默认时按照字典序来排的,所以可以直接调用STL中的sort函数。
/************************** https://www.acwing.com/problem/content/description/95/ Time 147 ms Memory 2448 KB **************************/ # include <iostream> # include <vector> # include <algorithm> int n; int m; std::vector<int> chosen; std::vector<std::vector<int>> save; void fsave() { save.push_back(chosen); } void print() { std::sort(save.begin(), save.end()); for (int i = 0; i < save.size(); i++) { for (int j = 0; j < save[i].size(); j++) { std::cout << save[i][j] << " "; } puts(""); } } void dfs(int step) { if (chosen.size() > m || chosen.size() + (n - step + 1) < m) { return; } if (step == n + 1) { fsave(); return; } dfs(step + 1); chosen.push_back(step); dfs(step + 1); chosen.pop_back(); } int main() { std::cin >> n >> m; dfs(1); print(); return 0; }