1.棋盘问题(dfs)
思路
1.dfs 参数里枚举每一行,然后在里面弄两个分支,选或者不选,选的话就枚举这一行的所有元素
2.注意最后一行要先判断,再返回
#include<iostream>
using namespace std;
const int N = 10;
char g[N][N];
int b[N];
int n, k, res;
// 枚举每一行,记录当前选了几个棋
void dfs(int u, int cnt){
if(cnt == k){
res++;
return;
}
// 这个要放在后面,防止没有判断最后一行
if(u > n) return;
// 选,枚举这一行的所有元素
for(int i = 1; i <= n; i++){
if(g[u][i] == '#' && !b[i]){
b[i] = 1;
dfs(u + 1, cnt + 1);
b[i] = 0;
}
}
// 不选
dfs(u + 1, cnt);
}
int main(){
while(cin>>n>>k && (n != -1 && k != -1)){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin>>g[i][j];
}
}
res = 0;
dfs(1, 0);
cout<<res<<endl;
}
return 0;
}
2.地牢大师(bfs)
思路
由二维扩展到了三维,多加一个 dz[] 数组
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
struct P{
int x, y, z;
};
char g[N][N][N];
int dis[N][N][N];
P q[N * N * N];
int l, r, c;
int dx[] = {1, -1, 0, 0, 0, 0};
int dy[] = {0, 0, 1, -1, 0, 0};
int dz[] = {0, 0, 0, 0, 1, -1};
int bfs(P st, P ed){
// 每次要清空队列
memset(q, 0, sizeof(q));
int hh = 0, tt = -1;
memset(dis, -1, sizeof(dis));
dis[st.x][st.y][st.z] = 0;
q[++tt] = st;
while(hh <= tt){
P t = q[hh++];
for(int i = 0; i < 6; i++){
int x = t.x + dx[i], y = t.y + dy[i], z = t.z + dz[i];
// 出界
if(x < 1 || x > l || y < 1 || y > r || z < 1 || z > c) continue;
// 遇到障碍物
if(g[x][y][z] == '#') continue;
// 已经走过
if(dis[x][y][z] != -1) continue;
dis[x][y][z] = dis[t.x][t.y][t.z] + 1;
if(x == ed.x && y == ed.y && z == ed.z) return dis[x][y][z];
q[++tt] = {x, y, z};
}
}
return -1;
}
int main(){
P st, ed;
while(cin>>l>>r>>c && (l || r || c)){
for(int i = 1; i <= l; i++){
for(int j = 1; j <= r; j++){
for(int k = 1; k <= c; k++){
cin>>g[i][j][k];
if(g[i][j][k] == 'S') st = {i, j, k};
if(g[i][j][k] == 'E') ed = {i, j, k};
}
}
}
int res = bfs(st, ed);
if(res == -1) cout<<"Trapped!"<<endl;
else cout<<"Escaped in "<<res<<" minute(s)."<<endl;
}
return 0;
}
3.抓住那头牛(bfs)
思路
bfs 搜索每种情况,最先到达 k 的时候,一定是最短的时间
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5;
pair<int, int> q[N + 10];
int hh, tt = -1;
int b[N + 10];
int n, k;
int bfs(int u){
q[++tt] = make_pair(u, 0);
while(hh <= tt){
auto it = q[hh++];
// 最先找到的一定是花费时间最短的
if(it.first == k) return it.second;
if(it.first + 1 >= 0 && it.first + 1 <= N && !b[it.first + 1]){
b[it.first + 1] = 1;
q[++tt] = make_pair(it.first + 1, it.second + 1);
}
if(it.first - 1 >= 0 && it.first - 1 <= N && !b[it.first - 1]){
b[it.first - 1] = 1;
q[++tt] = make_pair(it.first - 1, it.second + 1);
}
if(2 * it.first >= 0 && 2 * it.first <= N && !b[2 * it.first]){
b[2 * it.first] = 1;
q[++tt] = make_pair(2 * it.first, it.second + 1);
}
}
}
int main(){
scanf("%d%d", &n, &k);
if(n >= k){
printf("%d", n - k);
return 0;
}
int res = bfs(n);
printf("%d", res);
return 0;
}
4.翻转(dp)
思路
二进制枚举第一行所有情况,因为每一行都可以影响它的上一行
#include<iostream>
#include<cstring>
using namespace std;
const int N = 20;
int g[N][N], b[N][N];
int p[N][N], q[N][N];
int n, m;
void t(int x, int y){
int dx[] = {1, -1, 0, 0, 0}, dy[] = {0, 0, 1, -1, 0};
for(int i = 0; i < 5; i++){
int aa = x + dx[i], bb = y + dy[i];
if(aa < 0 || aa >= n || bb < 0 || bb >= m) continue;
g[aa][bb] ^= 1;
}
}
void dp(){
int res = 1e9;
// 枚举第一行所有情况
for(int k = 0; k < (1 << m); k++){
int cnt = 0;
memcpy(b, g, sizeof(g));
memset(p, 0, sizeof(p));
for(int i = 0; i < m; i++){
if(k >> i & 1){
t(0, i);
cnt++;
p[0][i] = 1;
}
}
for(int i = 1; i < n; i++){
for(int j = 0; j < m; j++){
if(g[i - 1][j]){
t(i, j);
cnt++;
p[i][j] = 1;
}
}
}
int flag = 1;
for(int i = 0; i < m; i++){
if(g[n - 1][i]){
flag = 0;
break;
}
}
if(flag){
if(cnt < res){
res = cnt;
memcpy(q, p, sizeof(p));
}
}
memcpy(g, b, sizeof(b));
}
if(res != 1e9){
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
printf("%d ", q[i][j]);
}
printf("\n");
}
}else printf("IMPOSSIBLE");
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
scanf("%d", &g[i][j]);
}
}
dp();
return 0;
}
5.找倍数(bfs)
思路
搜索每一位是 0 和 1,直到找到符合条件的数,队列可能会存储 2 的 100 次方个数,不能用数组模拟,100 之内的倍数会爆 int,记得开 long long
#include<iostream>
#include<queue>
using namespace std;
int n;
int main(){
while(cin>>n && n){
queue<long long> q;
// 因为是非 0 倍数 m,从 1 开始搜索
q.push(1);
while(q.size()){
long long it = q.front();
q.pop();
if(it % n == 0){
cout<<it<<endl;
break;
}
// 搜索 1
q.push(it * 10 + 1);
// 搜索 0
q.push(it * 10);
}
}
return 0;
}
6.质数路径(bfs)
思路
1.先用线性筛质数
2.再用搜索每一种情况,一共有 39 * 38 * 37… 种情况,首位不能为 0
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e4;
int primes[N], st[N];
int dis[N];
int t, a, b;
// 线性筛质数
void init(int n){
int cnt = 0;
for(int i = 2; i <= n; i++){
if(!st[i]) primes[cnt++] = i;
for(int j = 0; primes[j] <= n / i; j++){
st[primes[j] * i] = 1;
if(i % primes[j] == 0) break;
}
}
}
int main(){
init(9999);
scanf("%d", &t);
while(t--){
scanf("%d%d", &a, &b);
memset(dis, -1, sizeof(dis));
dis[a] = 0;
int q[N] = {0}, hh = 0, tt = - 1;
// 存储每一位
int num[4], sca[4] = {1000, 100, 10, 1};
q[++tt] = a;
while(hh <= tt){
int it = q[hh++];
// 存储每一位
for(int i = it, j = 3; i > 0; i /= 10, j--) num[j] = i % 10;
// 枚举39种情况
for(int i = 0; i < 4; i++){
for(int j = 0; j < 10; j++){
// 去掉首位为 0 的情况
if(!i && !j) continue;
// 改变第 i 位的数
int res = it + (j - num[i]) * sca[i];
// st 数组中被标记为 1 的就不是质数
if(!st[res] && dis[res] == -1){
q[++tt] = res;
dis[res] = dis[it] + 1;
}
}
}
}
printf("%d\n", dis[b]);
}
return 0;
}