1、青蛙跳杯子(BFS应用)
#include<bits/stdc++.h>
using namespace std;
string s1, s2;
map<string, int> ans;
queue<string> q;
int dir[] = { -3, - 2, - 1, 1, 2, 3 }; // 根据题意知,*有6种交换法
int bfs() {
q.push(s1);
ans[s1] = 0;
while (!q.empty()) {
string str = q.front();
q.pop();
int cnt = ans[str];
int idx = str.find('*');
for (int i = 0; i < 6; i++) {
int idx2 = idx + dir[i];
if (idx2 >= 0 && idx2 < s1.size()) {
swap(str[idx], str[idx2]);
if (!ans.count(str)) {
ans[str] = cnt + 1;
q.push(str);
if (str == s2) return ans[str];
}
swap(str[idx], str[idx2]);
}
}
}
return -1;
}
int main()
{
cin >> s1 >> s2;
cout << bfs() << endl;
return 0;
}
2、分考场
通过dfs搜索答案,注意要有剪枝来减少迭代次数
#include<bits/stdc++.h>
#define MAXN 150
using namespace std;
int n, m;
int a[MAXN][MAXN];
int p[MAXN][MAXN];
int ans = MAXN;
void dfs(int id, int classes) {
if (classes >= ans) return; // 剪枝
if (id > n) ans = min(ans, classes); // 保存答案
// 从已有的1-classes个考场中挑选
for (int i = 1; i <= classes; i++) {
int point = 1;
// 检查该考场中的是否有认识的人
while (p[i][point] && !a[id][p[i][point]]) point++;
if (p[i][point] == 0) {
p[i][point] = id;
dfs(id + 1, classes);
p[i][point] = 0;
}
}
// 已有的考场均有认识的人,故令开新的考场
p[classes+1][1] = id;
dfs(id + 1, classes+1);
p[classes+1][1] = 0;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int v, w;
cin >> v >> w;
a[v][w] = a[w][v] = 1;
}
dfs(1, 0);
cout << ans << endl;
return 0;
}
3、机器人塔
用DFS得到最下面一层的不同组合,则根据规则上面的层的组合确定并唯一,检查所有层的组合是否满足题意(cnta == a, cntb == b),满足题意则答案加1
#include<bits/stdc++.h>
#define MAXN 250050
using namespace std;
int a, b;
int layer;
char p[505][505];
int cnt = 0;
// 根据最下面一层的组合确定上面所有层的组合
void init() {
for (int i = layer; i >= 2; i--) {
for (int j = 1; j < layer; j++) {
if (p[i][j] == p[i][j + 1]) p[i - 1][j] = 'A';
else p[i - 1][j] = 'B';
}
}
}
// 检查所有层的组合是否符合题意
int check() {
int cnta = 0, cntb = 0;
for (int i = 1; i <= layer; i++) {
for (int j = 1; j <= i; j++) {
if (p[i][j] == 'A') ++cnta;
else ++cntb;
}
}
if (cnta == a && cntb == b) return 1;
else return 0;
}
void dfs(int n) {
// 当最后一层的组合确定
if (n > layer) {
init(); // 初始化所有层
if (check()) { // 检查该组合是否满足题意
cnt++;
}
return;
}
p[layer][n] = 'A';
dfs(n + 1);
p[layer][n] = 'B';
dfs(n + 1);
}
int main()
{
cin >> a >> b;
layer = sqrt(2 * (a + b));
dfs(1);
cout << cnt;
return 0;
}
4、填字母游戏
递归+记忆化搜索+博弈(能胜就胜,不能就平,否则才败)
#include<bits/stdc++.h>
#define MAXN 250050
#define ll long long
using namespace std;
int t;
string s;
map<string, int> mp;
char ch[2] = { 'L', 'O' };
int work(string str) {
if (mp.find(str) != mp.end()) return mp[str]; // 记忆化搜索,有答案直接用
// 一下三种情况可直接判断结果
if (str.size() < 3) {
mp[str] = 0;
return 0;
}
if (str.find("LO*") != -1 || str.find("L*L") != -1 || str.find("*OL") != -1) {
mp[str] = 1;
return 1;
}
if (str.find("*") == -1) {
mp[str] = 0;
return 0;
}
int flag = 0; // 记录是否能平局
int t;
for (int i = 0; i < str.size(); i++) {
if (str[i] == '*') {
for (int j = 0; j < 2; j++) {
str[i] = ch[j];
// //如果填完对方能赢,不符合最佳结果,回溯重找
if (str.find("LO*") != -1 ||str.find("L*L") != -1 || str.find("*OL") != -1) {
str[i] = '*';
continue;
}
t = work(str);
str[i] = '*';
if (t == -1) { // 对方输则表示自己赢
mp[str] = 1;
return 1;
}
else if (t == 1) { // //对方赢,回溯重找
continue;
}
else if (t == 0) {
flag = 1; // 能平局则平局
}
}
}
}
if (flag) {
mp[str] = 0;
return 0;
}
mp[str] = -1;
return -1;
}
int main()
{
cin >> t;
while (t--) {
cin >> s;
cout << work(s) << endl;
}
return 0;
}v
5、取球博弈
同上题,递归+记忆化搜索+博弈
#include<bits/stdc++.h>
#define MAXN 250050
#define ll long long
using namespace std;
int dp[1005][2][2]; // 记忆化数组,第一维记剩余球数,第二、三维记两者持球情况,0为偶,1为奇
int arr[3];
int dfs(int sum, int f, int s) {
if (dp[sum][f][s]) return dp[sum][f][s]; // 有答案直接返回
if (sum < arr[0]) { // 停止迭代的情况,即比赛结束
if ((f & 1) == (s & 1)) {
dp[sum][f][s] = 1;
return 1;
}
else if ((f & 1) && !(s & 1)) {
dp[sum][f][s] = 2;
return 2;
}
else if (!(f & 1) && (s & 1)) {
dp[sum][f][s] = -1;
return -1;
}
}
int flag = 0;
for (int i = 0; i < 3; i++) {
if (sum >= arr[i]) {
int tmp = dfs(sum - arr[i], s, (f + arr[i]) & 1);
if (tmp == -1) return dp[sum][f][s] = 2; // 对方输则自己赢,直接返回
else if (tmp == 1) flag = 1;// 有平局情况,先记录,继续循环,看看有没有胜利的可能
else continue; // 对方赢则自己输,继续循环,看看有没有平局或胜利的可能
}
else continue;
}
if (flag) {
dp[sum][f][s] = 1;
return 1;
}
// 无法胜利且平局则输
dp[sum][f][s] = -1;
return -1;
}
int main() {
cin >> arr[0] >> arr[1] >> arr[2];
sort(arr, arr + 3);
for (int i = 0; i < 5; i++) {
int n;
cin >> n;
int flag = dfs(n, 0, 0);
if (flag == 2) cout << '+' << " ";
else if (flag == 1) cout << 0 << " ";
else cout << '-' << " ";
}
return 0;
}
6、卡片换位(bfs)
将二维地图转化为一维字符串,不仅方便调用find来找元素位置和用swap来交换元素位置,还方便利用unordered_map<string, int>来记录地图对应走过的步数
#include<bits/stdc++.h>
#include<unordered_map>
#define MAXN 100050
#define MOD 1000000009
#define ll long long
using namespace std;
unordered_map<string, int> umap; // 记录地图对应走的步数
string s = "";
int a, b;
queue<string> q;
int dir[4][2] = { 1,0,0,1,-1,0,0,-1 };
void bfs() {
umap[s] = 0;
q.push(s);
while (!q.empty()) {
string u = q.front();
q.pop();
int dis = umap[u];
if (u.find('A') == b && u.find('B') == a) {
cout << dis << endl;
return;
}
int pos = u.find(' ');
int x = pos / 3, y = pos % 3;
for (int i = 0; i < 4; i++) {
int nx = x + dir[i][0], ny = y + dir[i][1];
if (nx >= 0 && nx <= 1 && ny >= 0 && ny <= 2) {
swap(u[pos], u[nx * 3 + ny]);
if (umap.count(u) == 0) {
umap[u] = dis + 1;
q.push(u);
}
swap(u[pos], u[nx * 3 + ny]);
}
}
}
}
int main(){
string tmp;
for (int i = 0; i < 2; i++) {
getline(cin, tmp);
s += tmp; // 将二维地图转化为一维字符串
b = s.find('B');
bfs();
return 0;
}
7、走方格
记忆化搜索,在返回方案数前记录下当前的方案数,以便下次再遍历到时能直接用
#include<bits/stdc++.h>
#define IL inline
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n, m;
int mp[35][35]; // mp[x][y]记录下(x,y)到(n,m)的方案数
int dfs(int x, int y) {
if (x > n || y > m) return 0;
if (x == n && y == m) return 1;
if (x % 2 == 0 && y % 2 == 0) return 0;
if (mp[x][y] != -1) return mp[x][y]; // 方案已存在,直接返回
return mp[x][y] = dfs(x + 1, y) + dfs(x, y + 1); // 记录下方案数
}
int main(){
cin >> n >> m;
memset(mp, -1, sizeof(mp));
cout << dfs(1, 1);
return 0;
}
8、大臣的旅费(dfs求树的直径)
求一颗树的直径=求一棵树中从任意节点到另一任意节点的最大距离,可以先通过dfs从根节点出发,找到距离根节点最远的节点p,然后再通过dfs从p出发,找到距离p的最远节点,同时记录最远距离,即为树的直径。
#include <bits/stdc++.h>
#define mod 1000000007
#define N 100005
#define ll long long
using namespace std;
struct node {
int to;
ll dis;
};
vector<vector<node>> tree;
int n;
ll maxlen;
int p;
ll ans[N];
void dfs(int v, int last, ll sum) {
if (sum > maxlen) {
maxlen = sum;
p = v;
}
for (int i = 0; i < tree[v].size(); i++) {
if (tree[v][i].to != last) {
dfs(tree[v][i].to, v, sum + tree[v][i].dis);
ans[v] = max(ans[v], sum + tree[v][i].dis);
}
}
}
int main() {
cin >> n;
tree.resize(n + 1);
for (int i = 1; i <= n - 1; i++) {
int u, v;
ll dis;
cin >> u >> v >> dis;
tree[u].push_back({ v, dis });
tree[v].push_back({ u,dis });
}
dfs(1, -1, 0);
dfs(p, -1, 0);
ll res = 0;
for (int i = 1; i <= n; i++) res = max(res, ans[i]);
cout << res * 10 + (res + 1) * res / 2;
return 0;
}
9、飞机降落(dfs)
本题看似贪心,实则数据量很小,可以用dfs爆搜
#include <bits/stdc++.h>
#define mod 1000000007
#define N 100005
#define ll long long
using namespace std;
int T;
int n;
struct plan {
int l, e, c;
};
int flag = 0;
vector<plan> table;
vector<int> vis;
void dfs(int time, int dep) {
if (dep == n) {
flag = 1;
return;
}
for (int i = 1; i <= n; i++) {
if (!vis[i] && table[i].e < time) return; // 剪枝,已经存在不能降落的飞机
if (!vis[i] && table[i].e >= time) {
vis[i] = 1;
dfs(max(time, table[i].l) + table[i].c, dep + 1);
if (flag == 1) return; // 答案已找到就直接返回
vis[i] = 0;
}
}
}
int main() {
cin >> T;
while (T--) {
flag = 0;
cin >> n;
table.resize(n + 1, { 0,0,0 });
table.assign(n + 1, { 0,0,0 });
vis.resize(n + 1, 0);
vis.assign(n + 1, 0);
for (int i = 1; i <= n; i++) {
int x, y, z;
cin >> x >> y >> z;
table[i].l = x, table[i].e = x + y, table[i].c = z;
}
dfs(0, 0);
if (flag == 1) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}