#include<iostream> usingnamespacestd; int n, k, sum, ans; int a[40]; voiddfs(int i, int cnt, int s){//i: 数组索引位置; cnt: 以选数据个数; s: 和 if (cnt > k) { //可行性剪枝,选择数的个数不可能超过k return; } if (s > sum) { //可行性剪枝,s大于sum的不用考虑 return; } if (i == n) { if (cnt == k && s == sum) { ans++; } return; } dfs(i + 1, cnt, s); //不选第i个数 dfs(i + 1, cnt + 1, s + a[i]); //选第i个数 } intmain(){ n = 30; k = 8; sum = 200; for (int i = 0; i < 30; i++) { a[i] = i + 1; } ans = 0; dfs(0, 0, 0); cout << ans << endl; return0; }
int n, m; string mp[110]; bool vis[110][110]; int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; constint INF = 0x3f3f3f3f; int ans = INF;
boolin(int x, int y) { return0 <= x && x < n && 0 <= y && y < m; }
voiddfs(int x, int y, intstep) { if (step > ans) { //不可能再是最优解,不用再算了 return; } if (mp[x][y] == 'T') { ans = step; return; } vis[x][y] = true; for (int i = 0; i < 4; i++) { int tx = x + dir[i][0]; int ty = y + dir[i][1]; if (in(tx, ty) && mp[tx][ty] != '*' && !vis[tx][ty]) { dfs(tx, ty, step + 1); } } vis[x][y] = false; }
intmain() { cin >> n >> m; for (int i = 0; i < n; i++) { cin >> mp[i]; } int x, y; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == 'S') { x = i, y = j; } } } dfs(x, y, 0); cout << ans << endl; }
voiddfs(int s, int cnt, int pos){ ... ... for (int i = pos; i <= n; i++) { if (!xuan[i]) { xuan[i] = true; dfs(s + a[i], cnt + 1, i + 1); // i + 1 表示从上一次选取的位置后面开始选 xuan[i] = false; } } }
奇偶性剪枝
有一个 n×m 大小的迷宫。其中字符'S'表示起点,字符'D'表示出口,字符'X'表示墙壁,字符'.'表示平地。你需要从'S'出发走到'D',每次只能向上下左右相邻的位置移动,并且不能走出地图,也不能走进墙壁。 每次移动消耗 1 时间,走过路都会塌陷,因此不能走回头路或者原地不动。现在已知出口的大门会在 T 时间打开,判断在 0 时间从起点出发能否逃离迷宫。
数据范围 n*,m≤10,*T≤50。
我们只需要用DFS来搜索每条路线,并且只需搜到 T 时间就可以了(这是一个可行性剪枝)。但是仅仅这样也无法通过本题,还需考虑更多的剪枝。 如上图所示,将 n×m 的网格染成黑白两色。我们记每个格子的行数和列数之和为 x,如果 x 为偶数,那么格子就是白色,反之奇数时为黑色。容易发现相邻的两个格子的颜色肯定不一样,也就是说每走一步颜色都会不一样。更普遍的结论是:走奇数步会改变颜色,走偶数步颜色不变。
那么如果起点和终点的颜色一样,而 T 是奇数的话,就不可能逃离迷宫。同理,如果起点和终点的颜色不一样,而 T 是偶数的话,也不能逃离迷宫。遇到这两种情况时,就不用进行DFS了,直接输出"NO"。
#include<iostream> #include<string> usingnamespacestd; constint N = 10; int n, m, T; string mp[N]; bool vis[N][N]; int dx[4] = {0, 0, -1, 1}; int dy[4] = {1, -1, 0, 0}; bool ok;
//x, y 坐标; t 时间 voiddfs(int x, int y, int t){ if (ok) return; //最优性剪枝 if (t == T) { if (mp[x][y] == 'D') ok = true; return; //可行性剪枝 } vis[x][y] = true;
for (int i = 0; i < 4; i++) { int tx = x + dx[i]; int ty = y + dy[i]; if (0 <= tx && tx < n && 0 <= ty && ty < m && mp[tx][ty] != 'X' && !vis[tx][ty]) { dfs(tx, ty, t + 1); } }
vis[x][y] = false; }
intmain(){ cin >> n >> m >> T; for (int i = 0; i < n; i++) { cin >> mp[i]; } int sx, sy, ex, ey; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == 'S') sx = i, sy = j; if (mp[i][j] == 'D') ex = i, ey = j; } } if ((sx + sy + ex + ey + T) % 2 != 0) { //奇偶性剪枝 cout << "NO" << endl; } else { ok = false; dfs(sx, sy, 0); if (ok) { cout << "YES" << endl; } else { cout << "NO" << endl; } } return0; }
int n, m; char mp[1000][1000]; bool row[1010], col[1010]; //标记行和列是否被访问过 voiddfs(int x, int y){ mp[x][y] = '0'; if (!row[x]) { row[x] = true; // 若该行没有被访问过,则这次引爆该行 for (int i = 0; i < m; i++) { if (mp[x][i] == '1') { dfs(x, i); } } } if (!col[y]) { col[y] = true; // 若该列没有被访问过,则这次引爆该列 for (int i = 0; i < n; i++) { if (mp[i][y] == '1') { dfs(i, y); } } } }
intmain(){ cin >> n >> m; for (int i = 0; i < n; i++) { cin >> mp[i]; } int cnt = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == '1') { cnt++; dfs(i, j); } } } cout << cnt << endl; }
找数字 - (抽象dfs + 剪枝)
给一个数 n,让你找出一个只由 0,1 组成的十进制数 m,要求这个正整数 m 可以被 n 整除。 输入格式: 输入一个整数 n (1≤n<200)。 输出格式: 对于输入整数 n 的每一个值,输出 m 的相应值,保证有一个数字长度小于 19 位的数字。如果有一个给定值 n 有多个解,其中任何一个都是可以接受的。 本题答案不唯一,符合要求的答案均正确 样例输入复制:
int n; bool vis[10]; //n个位置,从1~n中选一个数塞进去, voiddfs(int num, int cnt){ if (cnt == n) { cout << num << endl; return; //小心别少啦! } for (int i = 1; i <= n; i++) { //对于每一个位置,去枚举每一个可以用的数 if (!vis[i]) { //如果之前没有被用过 vis[i] = true; //标记使用该数 dfs(num * 10 + i, cnt + 1); //对于这种全是数字的用,当成数字拼接好了 vis[i] = false; //去试探别的数前,要取消对该数的标记 } } }
intmain(){ cin >> n; int ans = 1; for (int i = 1; i <= n; i++) { ans *= i; } cout << ans << endl; dfs(0, 0); return0; }
蒜头君的旅游计划
马上就要放寒假了,蒜头君已经辛勤的工作一年了,打算和家人一起出去旅游放松放松。有经验的人一定知道,出去旅游前一定要有详细的旅行规划。蒜头君打算旅游 n 个城市,但并不是每条路线的花费都是一样的。蒜头君是一个勤俭节俭的人,他就在想怎么把所有的城市旅游一遍花费最小呢?(蒜头君不喜欢重复旅游同一个城市,也就是说已到达过的城市蒜头君是不会再次光临的)
#include<iostream> #include<algorithm> usingnamespacestd; int G[20][20]; bool vis[20]; int n, ans = 0x3f3f3f; // u:当前所在点 cnt:所走步数 sum:总花费 voiddfs(int u, int cnt, int sum){ if (sum > ans) { //最优性剪枝 return; } if (cnt == n - 1) { ans = min(ans, sum + G[u][0]); } vis[u] = true; //标记在循环外,包含源点,循环n-1次,到达目的 for (int i = 0; i < n; i++) { if(!vis[i]) { dfs(i, cnt + 1, sum + G[u][i]); } } vis[u] = false; }
intmain(){ cin >> n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> G[i][j]; } } dfs(0, 0, 0); cout << ans; return0; }