1 广搜

1)ab路线

https://www.luogu.com.cn/problem/P9425

特殊的是 这里定义了queue<array<int, 3>> q;队列里每个点是一个数组,这样能保存上一个点的状态。

我们不需要判断走的这个是a是b,只需要判断和上一个走的点的关系(上一个走过的点更好取)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n, m, k, dx[] = { 0, 1, 0, -1 }, dy[] = { -1, 0, 1, 0 }, ans;
bool st[1001][1001][11];
int main() {
    cin >> n >> m >> k;
    vector<string> s(n);
    for (int i = 0;i < n;i++) cin >> s[i];
    queue<array<int, 3>> q;
    q.push({ 0, 0, 1 });
    st[0][0][1] = true;
    while (q.size()) {
        int size = q.size();
        while (size--) {
            auto p = q.front();
            q.pop();
            int x = p[0], y = p[1];
            if (x == n - 1 && y == m - 1) {
                cout << ans;
                return 0;
            }
            if (p[2] == k) {
                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 && s[a][b] != s[x][y] && !st[a][b][1]) {
                        st[a][b][1] = true;
                        q.push({ a, b, 1 });
                    }
                }
            }
            else {
                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 && s[a][b] == s[x][y] && !st[a][b][p[2] + 1]) {
                       
                        //为什么st三维,因为不同k走到一个点是不一样的,有的步数可能不能到达。
                        //虽然都是一个点,但是不一样。普通的广搜如果是一个点多次出现,那前面的肯定能到大,所以当时是二维。在这道题目中,每个格子最多可以被访问 k 次。
                        st[a][b][p[2] + 1] = true;
                        q.push({ a, b, p[2] + 1 });
                    }
                }
            }
        }
        ans++;  相当于这一层都入队了,都走完了,该走下一层了,所以++
    }
    cout << -1;
    return 0;
}

自己写的ac
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
queue<array<int, 3>> q;
bool st[maxn][maxn][12];
string g[maxn];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
int n, m, k;
string s[maxn];
int ans;
bool flag;
void bfs(int a,int b)
{
	st[a][b][1] = true;
	q.push({ a,b,1 });
	while (q.size())
	{
		int size = q.size();
		while (size--)
		{
			auto t = q.front();
			q.pop();
			int x = t[0];
			int y = t[1];
			int cnt = t[2];
			if (x == n - 1 && y == m - 1) {
				cout << ans;
				flag = true;
				return;
			}
			if (cnt == k) {
				for (int i = 0;i < 4;i++)
				{
					int x1 = x + dx[i];
					int y1 = y + dy[i];
					if (st[x1][y1][1])continue;
					if (x1 < 0 || y1 < 0 || x1 >= n || y1 >= m)continue;
					//注意要进行越界检查
					if (s[x][y] == s[x1][y1])continue;
					st[x1][y1][1] = true;
					q.push({ x1,y1,1 });
				}
			}
			else {
				for (int i = 0;i < 4;i++)
				{
					int x1 = x + dx[i];
					int y1 = y + dy[i];
					if (st[x1][y1][t[2]+1])continue;
					//注意检查条件,注意边界谁是行谁是列
					if (x1 < 0 || y1 < 0 || x1 >= n || y1 >= m)continue;
					if (s[x][y] != s[x1][y1])continue;
					st[x1][y1][t[2]+1] = true;
					q.push({ x1,y1,t[2]+1});
				}
			}

		}
		ans++;
	}

}
signed main()
{
	cin >> n >> m >> k;
	for (int i = 0;i < n;i++)
		cin >> s[i];
	bfs(0, 0);
	if (!flag)cout << -1 << endl;
}

2)旋转九宫格

https://www.luogu.com.cn/problem/P10578

点击查看代码
#include <bits/stdc++.h>
using namespace std;
string t = "123456789";
//二维变成一维
map<string, int> h;
//用map存的是否访问过
void bfs()
{
	queue<string> q;
	q.push(t);
	//目标状态加入队列,目标状态的步数设为1
	h[t] = 1;
	while (q.size())
	{
		string u = q.front();
		q.pop();
		string v[4] = { u,u,u,u };
		//生成 4 个新状态 v[0] 到 v[3],分别对应 33 九宫格中 4 个 22 子矩阵逆时针旋转后的状态
		v[0][0] = u[1], v[0][1] = u[4], v[0][3] = u[0], v[0][4] = u[3]; // 左上角
		v[1][1] = u[2], v[1][2] = u[5], v[1][4] = u[1], v[1][5] = u[4]; // 右上角
		v[2][3] = u[4], v[2][4] = u[7], v[2][6] = u[3], v[2][7] = u[6]; // 左下角
		v[3][4] = u[5], v[3][5] = u[8], v[3][7] = u[4], v[3][8] = u[7]; // 右下角
		// 注意这是一个逆推的过程,要逆时针转
		//对于每个新状态 v[i],若该状态未被访问过(即 h[v[i]] 为 0),则将其步数设为当前状态 u 的步数加 1,然后把该状态加入队列
		//相当于把这个矩阵每一次旋转都枚举出来,东西南北加入,然后再遍历(东)东西南北
		for (int i = 0; i < 4; i++)
			if (!h[v[i]])
			{
				h[v[i]] = h[u] + 1;
				if (v[i] == t) break;
				q.push(v[i]);
			}
	}
}

int main()
{
	int _ = 1;
	scanf("%d", &_);
	bfs(); // 从终点开始预找出所有可能结果的步数
	while (_--)
	{
		string s;
		for (int i = 0; i < 9; i++)
		{
			char c;
			scanf(" %c", &c);
			s += c;
		}
		printf("%d\n", h[s] - 1);
	}
	return 0;
}



3)全球变暖

不懂

https://www.luogu.com.cn/problem/P8662

4)岛屿个数

https://www.lanqiao.cn/problems/3513/learning/

不懂:为啥海水要搜索八个方向,不然遍历不到所有外海。

多个测试样例之间忘记重置,还有多个广搜不能用一个队列

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 55;
int m, n,ans;
char g[maxn][maxn];
int st[maxn][maxn];
int dx[] = { 0,0,1,-1,1,1,-1,-1 };
int dy[] = { 1,-1,0,0,-1,1,1,-1 };
void dfs2(int a, int b)
{
	queue<pii> q;
	st[a][b] = 1;
	q.push({ a,b });
	while (q.size())
	{
		auto t = q.front();
		int x = t.first;
		//不用加括号!!!
		int y = t.second;
		q.pop();
		for (int i = 0;i < 4;i++)
		{
			int x1 = dx[i] + x;
			int y1 = dy[i] + y;
			if (st[x1][y1] == 1)continue;
			if (x1<0 || y1<0 || x1>m+1|| y1>n+1)continue;
			if (g[x1][y1] == '0')continue;

				q.push({ x1,y1 });
				st[x1][y1] = 1;
			

		}
	}
	return;
}
void dfs1(int a, int b)
{
	queue<pii> q;
	//定义全局的队列是不对的
	st[a][b] = 1;
	q.push({ a,b });
	while (q.size())
	{
		auto t = q.front();
		int x =t.first;
		int y =t.second;
		q.pop();
		for (int i = 0;i < 8;i++)
		{
			int x1 = dx[i] + x;
			int y1 = dy[i] + y;
			if (st[x1][y1] == 1)continue;
			if (x1<0 || y1<0 || x1>m+1  || y1>n+1 )continue;

			//因为两边各添了0,所以应该是>n+1;
			if (g[x1][y1] == '1')
			{
				dfs2(x1, y1);
				ans++;
			}
			else {
q.push({ x1,y1 });
st[x1][y1] = 1;
//不管是海水还是岛屿只要访问过就不用访问了,不需要设置两个st数组
//否则dfs2时还需要判断st2麻烦
			}
			
		}
	}
cout << ans << endl;
}
void solve()
{
	
	memset(g, '0', sizeof g);
	memset(st, 0, sizeof st);
	ans = 0;
	//既没有重置ans,也没用重置st!!!!
	//导致前一次测试的访问状态残留
	//忘记在周围添加一圈海水,因为是字符,所以不会默认填0,而是填空
	cin >> m>> n;
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
		{
			cin >> g[i][j];
		}
	//注意这里的输入是连着的
	dfs1(0, 0);
	

}
signed main()
{
	int T;
	cin >> T;
	while (T--)
		solve();
	return 0;
}
点击查看代码
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 55;
typedef pair<int, int> PII;
char g[N][N];
bool st[N][N];
int T, n, m;
int cnt;
// 8 连通方向数组
int dx[8] = { 1, 0, 0, -1, 1, 1, -1, -1 };
int dy[8] = { 0, 1, -1, 0, 1, -1, 1, -1 };

void bbfs(int x, int y) // 搜陆地的板子(4 连通)
{
    queue<PII> q;
    q.push({ x, y });
    st[x][y] = 1;
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) // 四个方向
        {
            int a = t.first + dx[i];
            int b = t.second + dy[i];
            if (a >= 0 && a <= n + 1 && b >= 0 && b <= m + 1 && !st[a][b]) // 在合法区间里且没搜过的
            {
                if (g[a][b] == '1') // 是陆地就进队标记
                {
                    q.push({ a, b });
                    st[a][b] = 1;
                }
            }
        }
    }
}

void bfs(int x, int y) // 搜海水的板子(8 连通)
{
    queue<PII> q;
    q.push({ x, y });
    st[x][y] = 1;
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < 8; i++) // 八个方向
        {
            int a = t.first + dx[i];
            int b = t.second + dy[i];
            if (a >= 0 && a <= n + 1 && b >= 0 && b <= m + 1 && !st[a][b])
            {
                if (g[a][b] == '0') // 是海进队
                {
                    q.push({ a, b });
                    st[a][b] = 1;
                }
                else // 不是海就是陆地呗,给陆地染色标记,顺便计数
                {
                    bbfs(a,b);
                    cnt++;
                }
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> T;
    while (T--)
    {
        memset(st, 0, sizeof st); // 因为是有好几组测试数据,每次都要初始化
        memset(g, '0', sizeof g);
        cnt = 0;

        cin >> n >> m;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                cin >> g[i][j];
            }
        // 在原地图外阔一圈海水
        // 防止角落 岛屿卡住搜索的范围
        bfs(0, 0); // 直接从外圈海水搜,把海水都搜到后标记,剩下的就是岛
        cout << cnt << endl;
    }
    return 0;
}

2 并查集

int p[maxn];
int find(int x)
{
	if (p[x] == x)return x;
	return p[x] = find(p[x]);
	//这里用find等于找到了父亲的祖宗,因为找的时候递归了find
}
void merge(int x, int y) {
	if (find(x) != find(y))
		p[find(x)] = find(y);
}

1)不重复数组

https://www.luogu.com.cn/problem/P8686

因为后面遇到重复的是给ai++,而不能是比他小,所以我们可以逆向思维,每遇到重复的就给祖宗++。祖宗代表后面遇到这个树应该设置为几,在这个过程中有的集合合并了

自己写的代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e5+10;
int a[maxn],p[maxn];
int n;
int find(int x)
{
	if (p[x] == x)return x;
	//又把判等写成赋值
	else return p[x] = find(p[x]);
}
signed main()
{
	cin >> n;
	for (int i = 1;i <= n;i++)
	{cin >> a[i];

	}
	for (int i = 1;i <= maxn;i++)
	{
		p[i] = i;
		//不能只赋值数组,要不然数组增加到了4,但是4的父亲是0
		//1 3 3 6 7
			
	}
	for (int i = 1;i <= n;i++)
	{
	
		cout << p[find(a[i])]++ << " ";
		}


}


点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn = 1e5 + 10;
int a[maxn];
int p[maxn];
int find(int x)
{
	
	if (p[x] == x)return x;
	return p[x] = find(p[x]);
	//这里用find等于找到了父亲的祖宗,因为找的时候递归了find
}
void merge(int x, int y) {
	if (find(x) != find(y))
		p[find(x)] = find(y);
}
int main()
{
	cin >> n;
	int x;
	for (int i = 1;i <= n;i++)
	{
		cin >> a[i];
	}
	for (int i = 1;i <= maxn;i++)
	{
		p[i] = i;
		//不能只赋值数组,要不然数组增加到了5,但是5的父亲是0
	}
	for (int i = 1;i <= n;i++)
	{
		a[i]=p[find(a[i])]++;

		//相当于在增加时把集合合并了,2233 2=2 2的父亲设为3  2=3 3的父亲设为4 3=4
	}
	for (int i = 1;i <= n;i++)
	{
		cout << a[i] << " ";

	}
}

2)合并植物

https://www.luogu.com.cn/problem/P8654

最简单的并查集应用

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int a[maxn];
int p[maxn];
int st[maxn];
int ans;
int n,m;
int k;
int find(int x)
{
	
	if (p[x] == x)return x;
	return p[x] = find(p[x]);
	//这里用find等于找到了父亲的祖宗,因为找的时候递归了find
}
void merge(int x, int y) {
	if (find(x) != find(y))
		p[find(x)] = find(y);
}
int main()
{
	for (int i = 1;i <= maxn-10;i++)
		p[i] = i;
	cin >> m >> n;
	cin >> k;
	int x, y;
	while (k--)
	{
		cin >> x >> y;
		merge(x, y);
	}
	for (int i = 1;i <= m * n;i++)
	{

		if (st[find(i)] == 0)
		{ans++;
		st[find(i)] = 1;
		}
			

	}
	cout << ans << endl;

}

3)兔子找同伴

https://www.luogu.com.cn/problem/P10577

对问题的简化思考,先不管它在哪个位置,最终一定是,要么和左边要么和右边,这个取决于谁近。然后分类,假设左边找右边,要么相互靠近,要么一起向右,相互靠近的就是中点是答案。一起向右的那右边的同伴就是左边的同伴

点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
#define p first
#define no second
using namespace std;
const int N = 1e5 + 10;
typedef pair<int, int> PII;
int n;
PII t[N];//每只兔子的位置和编号
int fa[N];//fa[i] 表示第 i 只兔子的父节点
int ans[N];//每只兔子完成集结后的最终位置。
int find(int x) // 并查集+路径压缩
{
	if (x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &t[i].p);
		t[i].no = i;
	}
	sort(t + 1, t + n + 1); // 按位置排序
	fa[1] = 2, fa[n] = n - 1; // 特判兔子1与n,1的祖宗肯定是2

	for (int i = 2; i < n; i++)
	{
		if (t[i].p - t[i - 1].p <= t[i + 1].p - t[i].p) fa[i] = i - 1;
		else fa[i] = i + 1;
	}

	for (int i = 1; i <= n; i++)
	{
		if (fa[i] == i + 1 && fa[i + 1] == i) // i与i+1双向奔赴
		{
			t[i].p = t[i + 1].p = (t[i].p + t[i + 1].p) / 2;
			fa[i] = i, fa[i + 1] = i + 1;//为啥要更新父节点为自身:因为我们要区分双向奔赴的兔子和普通兔子
		}
	}
	// 其余兔子最终到达他祖宗的位置
	for (int i = 1; i <= n; i++)
		if (fa[i] != i) t[i].p = t[find(i)].p;
	for (int i = 1; i <= n; i++)
	
	//因为t数组排序了,不是输入时的顺序,所以我们有no保留了输入顺序
		ans[t[i].no] = t[i].p;
	for (int i = 1; i <= n; i++)
		printf("%d ", ans[i]); // 还原答案

	return 0;
}
posted on 2025-03-30 22:06  Hoshino1  阅读(9)  评论(0)    收藏  举报