递归实现各类枚举
最近按算阶一起写吧,定好小计划;
1.递归实现指数型枚举;
从1~n中任意算出几个数,输出所有可能的方案;
这等价于选或不选,方案数2的n次方;所以递归分支选或不选,将尚未确定的整数数量减少1,从而转化为规模更小的问题;
#include<bits/stdc++.h> using namespace std; int n; vector<int> chosen; void calc(int x) { if(x==n+1) { for(int i=0;i<chosen.size();i++) printf("%d ",chosen[i]); puts(" "); return ; } //不选x分支; calc(x+1); //选x分支; chosen.push_back(x);//记录x已被选择; calc(x+1);//求解子问题; chosen.pop_back();//准备回溯到上一问题之前,还原现场; } int main() { cin>>n; calc(1); return 0; }
2.递归实现组合型枚举;
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n>0, 0<=m<=n, n+(n-m)<=25。
加上一个剪枝,时间复杂度降低(选够m个或剩下都选都不够m个);
#include<bits/stdc++.h> using namespace std; int n,m; vector<int> chosen; void calc(int x) { if(chosen.size()>m||chosen.size()+(n-x+1)<m) { return ; } if(x==n+1) { for(int i=0;i<chosen.size();i++) printf("%d ",chosen[i]); puts(" "); return ; } //选x分支; chosen.push_back(x);//记录x已被选择; calc(x+1);//求解子问题; chosen.pop_back();//准备回溯到上一问题之前,还原现场; calc(x+1);//不选x分支; } int main() { cin>>n>>m; calc(1); return 0; }
3.递归实现排列型枚举;
把 1~n 这 n(n<10) 个整数排成一行后随机打乱顺序,输出所有可能的次序。
所谓的全排列问题;n!种方案数,递归中,我们将可用的数作为数列中下一个数,求解剩余n-1个数作为子问题;
#include<bits/stdc++.h> using namespace std; int order[20];//按顺序依次标记被选择的整数; bool chosen[20];//标记被选择的数; int n; void calc(int k) { if(k==n+1)//问题边界; { for(int i=1;i<=n;i++) printf("%d ",order[i]); puts(" "); return ; } for(int i=1;i<=n;i++) { if(chosen[i]) continue; order[k]=i; chosen[i]=1; calc(k+1); chosen[i]=0; order[k]=0;//可省略; } } int main() { cin>>n; calc(1);
return 0; }
例题:费解的开关;(指数型枚举)
分析性质
1.每个位置至多被点击一次;
2.若固定第一行(不再改变),则满足的方案数至多有1种;
原因:第i行确定,只有点击i+1行才能使其变为0,;
3.点击的顺序不影响最终的结果,
于是我们考虑第一行,枚举2的五次方=32次;从第一行开始递推,若第i行为1,则点击i+1行的该位置,若到达第n行不全为0,则方案不合法;
枚举第一行,可以采用位运算;
#include <map> #include <set> #include <list> #include <cmath> #include <deque> #include <queue> #include <stack> #include <cctype> #include <cstdio> #include <bitset> #include <string> #include <vector> #include <complex> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #include <functional> const int maxn = 5e5 + 100; const int inf = 0x3f3f3f3f; const int dir[5][2]={0,0,1,0,-1,0,0,1,0,-1}; typedef long long LL; using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=f; } template<typename T> inline void write(T s) { if (s < 0) putchar('-'), s = -s; if (s > 9) write(s / 10); putchar(s % 10 + '0'); } int n,ans,res; int a[20][20],g[20][20]; bool flag; inline void turn(int x, int y) { for (int k = 0; k < 5; ++k) g[x + dir[k][0]][y + dir[k][1]] ^= 1; } int main() { read(n); while (n--) { for (int i = 1; i <= 5; ++i) { for (int j = 1; j <= 5; ++j) { char ch; cin >> ch; a[i][j] = (int)(ch - '0'); } } ans = inf; for (int k = 1; k <= 32; ++k) { for (int i = 1; i <= 5; ++i) for (int j = 1; j <= 5; ++j) g[i][j] = a[i][j]; res = 0; for (int i = 0; i < 5; ++i) { if ((k >> i) & 1) { ++res; turn(1, i + 1); } } for (int i = 2; i <= 5; ++i) { for (int j = 1; j <= 5; ++j) { if (g[i - 1][j] == 0) { ++res; turn(i, j); } } } flag = true; for (int i = 1; i <= 5; ++i) { if (g[5][i] == 0) { flag = false; break; } } if (flag) ans = min(ans, res); } if (ans > 6) ans = -1; write(ans), putchar('\n'); } return 0; }