// 递归实现组合型枚举 #include<iostream> #include<cstring> #include<algorithm> usingnamespacestd; constint N = 30; int n, m; int way[N]; voiddfs(int u, int start){ if (u + n - start < m) return; // 剪枝 if (u == m + 1) { for (int i = 1; i <= m; i++) printf("%d ", way[i]); puts(""); return; } for (int i = start; i <= n; i++) { way[u] = i; dfs(u+1, i+1); way[u] = 0; } } intmain(){ scanf("%d%d", &n, &m); // 从n中选m个数 dfs(1, 1); // 从第1个位置开始枚举,从第1个数开始枚举 return0; }
#include<iostream> #include<algorithm> #include<cstring> usingnamespacestd; constint N = 40; bool st[N], backup[N]; int n; int ans;
boolcheck(int a, int c){ int b = c*n - c*a; // 公式 if (!a || !b || !c) returnfalse; // abc均不能为0 // 备份 memcpy(backup, st, sizeof st); // 分析b的组成 while(b) { int x = b % 10; b /= 10; // b中不能出现0且x不存在于ac中 if (!x || backup[x]) returnfalse; backup[x] = true; } // 1-9之间的数必须全部用到 for (int i = 1; i <= 9; i++) if (!backup[i]) returnfalse; returntrue; } // 枚举c的取值 voiddfs_c(int u, int a, int c){ if (u > 9) return; // 数字都已枚举到则退出 if (check(a, c)) ans++; // 用ac计算出b判断是否合法,合法则方案数+1 // 排列型枚举 for (int i = 1; i <= 9; i++) { if (!st[i]) { st[i] = true; dfs_c(u+1, a, c * 10 + i); st[i] = false; } } } // 枚举a的取值 voiddfs_a(int u, int a){ // 因为bc不能为0,所以a必须小于n if (a >= n) return; if (a) dfs_c(u, a, 0); // 剪枝,嵌套 // 全排列枚举 for (int i = 1; i <= 9; i++) { if (!st[i]) { st[i] = true; dfs_a(u+1, a * 10 + i); st[i] = false; } } } intmain(){ scanf("%d", &n); dfs_a(0, 0); //已经用了多少个数字,从值为0开始枚举 cout << ans << endl; return0; }
递推
先算子问题,然后用子问题去回答原问题。递归的逆过程
简单斐波那契
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include<iostream> #include<cstring> #include<algorithm> usingnamespacestd; intmain(){ int n; cin >> n; int a = 0, b = 1; for (int i = 1; i <= n; i++) { cout << a << ' '; int fn = a + b; a = b, b = fn; } return0; }
voidturn(int x, int y){ for (int i = 0; i < 5; i++) { int a = x + dx[i], b = y + dy[i]; if (a < 0 || a >= 5 || b < 0 || b >= 5) continue; g[a][b] ^= 1; } }
intmain(){ int T; cin >> T; while(T--) { for(int i = 0; i < 5; i++) cin >> g[i]; int res = 10; for (int i = 0; i < (1 << 5); i++) { // 枚举第一行所有可能的操作方法 memcpy(backup, g, sizeof g); // 保存最初始的状态 int step = 0; // 记录操作次数 for (int j = 0; j < 5; j++) { if (i >> j & 1) { turn(0, j); step++; } } for (int i = 0; i < 4; i++) //后面的每一行中的每一格是否操作,取决于它的上一格 for (int j = 0; j < 5; j++) { if (g[i][j] == '0') { turn(i+1, j); step++; } } bool isdark = false; // 遍历最后一行是否全亮即可得到答案 for (int i = 0; i < 5; i++) if (g[4][i] == '0') isdark = true; if (!isdark) res = min(res, step); memcpy(g, backup, sizeof backup); // 还原成最初始的状态 } if (res > 6) res = -1; cout << res << endl; } return0; }
// 前缀和 #include<iostream> #include<cstring> usingnamespacestd; typedeflonglong LL; constint N = 1e5 + 10; int n; int a[N], b[N], c[N]; int s[N], cnt[N]; int va[N]; // va[i]表示在a[]中有多少个数小于b[i] int vc[N]; // vc[i]表示在c[]中有多少个数大于b[i]
intmain(){ scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]), a[i]++; // 所有数加1,便于前缀和 for (int i = 0; i < n; i++) scanf("%d", &b[i]), b[i]++; for (int i = 0; i < n; i++) scanf("%d", &c[i]), c[i]++; // 计算va[i],代表比b[i]小的数的个数 for (int i = 0; i < n; i++) cnt[a[i]]++; for (int i = 1; i < N; i++) s[i] = s[i-1] + cnt[i]; for (int i = 0; i < n; i++) va[i] = s[b[i]-1]; memset(s, 0, sizeof s); memset(cnt, 0, sizeof cnt); // 计算vc[i],代表比b[i]大的数的个数 for (int i = 0; i < n; i++) cnt[c[i]]++; for (int i = 1; i < N; i++) s[i] = s[i-1] + cnt[i]; for (int i = 0; i < n; i++) vc[i] = s[N-1] - s[b[i]]; LL res = 0; // 枚举b[i] for (int i = 0; i < n; i++) res += (LL) va[i]*vc[i]; cout << res << endl; return0; }
#include<iostream> #include<cstring> usingnamespacestd; constint N = 25; char g[N][N]; bool st[N][N]; int n, m; int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
intdfs(int x, int y){ int res = 1; st[x][y] = true; for (int i = 0; i < 4; i++) { int a = x + dx[i], b = y + dy[i]; if (a < 0 || a >= n || b < 0 || b >= m) continue; if (st[a][b]) continue; if (g[a][b] != '.') continue; res += dfs(a, b); } return res; }
intmain(){ while (cin >> m >> n, n || m) { for (int i = 0; i < n; i++) cin >> g[i]; int x, y; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (g[i][j] == '@') { x = i, y = j; break; } memset(st, false, sizeof st); cout << dfs(x, y) << endl; } return0; }
#include<iostream> #include<cstring> #include<algorithm> usingnamespacestd; typedeflonglong LL; constint N = 3; int n, m; // 1x3*3x3 voidmul(int c[], int a[], int b[][N]){ int temp[N] = {0}; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) temp[i] = (temp[i] + (LL)a[j] * b[j][i]) % m; memcpy(c, temp, sizeof temp); } // 3x3*3x3 voidmul(int c[][N], int a[][N], int b[][N]){ int temp[N][N] = {0}; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int k = 0; k < N; k++) temp[i][j] = (temp[i][j] + (LL)a[i][k] * b[k][j]) % m; memcpy(c, temp, sizeof temp); }
intmain(){ scanf("%d%d", &n, &m); int f[N] = {1, 1, 1}; int A[N][N] = { {0, 1, 0}, {1, 1, 1}, {0, 0, 1}, }; n--; // 快速幂 while (n) { if (n & 1) mul(f, f, A); mul(A, A, A); n >>= 1; } printf("%d\n", f[2]); return0; }
树状树组
可更新的前缀和
主要用处为:
给某一个位置加上一个数(单点修改)
求某一个前缀和(区间查询)
两个操作时间复杂度均为O(logn)
x在第k层取决于其二进制末尾有k个0
lowbit(x)=x&−x=x&(∼x+1)=2k
树状数组C[x]=(x−lowbit(x),x]=(x−2k,x]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
int a[N], tr[N]; // 树状数组下标从1开始存储 // 2^k intlowbit(int x){ return x & -x; } // 单点修改,为某一个位置加上一个数,同步变化之后的值 voidadd(int x, int v){ for (int i = x; i <= n; i += lowbit(i)) tr[i] += v; } // 区间查询,1~x之间的和 intquery(int x){ int res = 0; for (int i = x; i; i -= lowbit(i)) res += tr[i]; return res; }