包含洛谷和vjudgekuangbin题目(慢慢更新中)
洛谷
1.马的遍历
题目链接:https://www.luogu.com.cn/problem/P1443
题意:给你一个n×m的棋盘,然后给你马的起始位置(x,y),问题马到棋盘上所有点的最小步数,到达不了的就是-1。
思路:首先我们知道马能走日,把它的坐标模拟出来就可以了。
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y;
queue<pair<int, int> > q;
int mp[410][401];
bool vis[410][410];
int dx[8] = {
2,-2,2,-2,-1,1,-1,1}, dy[8] = {
1,1,-1,-1,2,2,-2,-2};
signed main()
{
cin >> n >> m >> x >> y;
memset(mp, -1, sizeof mp);
mp[x][y] = 0;
vis[x][y] = 1;
q.push({
x, y});
while (!q.empty())
{
int x = q.front().first, y = q.front().second;
q.pop();
for (int i = 0; i < 8; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && vis[tx][ty] == 0)
{
vis[tx][ty] = 1;
q.push({
tx, ty});
mp[tx][ty] = mp[x][y] + 1;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
printf("%-5d", mp[i][j]);
cout << endl;
}
return 0;
}
2.好奇怪的游戏
题目链接:https://www.luogu.com.cn/problem/P1747
题意:给了你两匹马的坐标(x1,y1)(x2,y2),它们可以走日也可以走田,让你求它们到(1,1)的最小步数。
思路:同上
#include <bits/stdc++.h>
using namespace std;
int dx[12] = {
2,2,-2,-2,-1,-1,1,1,-2,-2,2,2};
int dy[12] = {
2,-2,2,-2,-2,2,-2,2,1,-1,1,-1};
struct Node
{
int x, y, step;
};
queue<Node> q;
bool vis[25][25];
int nx, ny;
int bfs(int x, int y)
{
q.push(Node{
x, y, 0});
vis[x][y] = 1;
while (!q.empty())
{
Node p = q.front(); q.pop();
for (int i = 0; i < 12; i ++)
{
int tx = p.x + dx[i], ty = p.y + dy[i];
if (tx > 0 && ty > 0 && tx <= 50 && ty <= 50 && vis[tx][ty] == 0)
q.push((Node){
tx, ty, p.step + 1}), vis[tx][ty] = 1;
if (tx == nx && ty == ny) return p.step + 1;
}
}
}
int main()
{
cin >> nx >> ny;
cout << bfs(1, 1) << endl;
memset(vis, 0, sizeof vis);
while (!q.empty()) q.pop();
cin >> nx >> ny;
cout << bfs(1, 1) << endl;
return 0;
}
3.血色先锋队
题目链接:https://www.luogu.com.cn/problem/P1332
题意:地图是一个n×m的矩阵,然后有a个感染源,告诉了你a个感染源的位置,b个领主,让你求b个领主被感染的最短时间。
思路:就是找到a个感染源的位置,进行扩散,记录b个领主第一次被感染的时间即可。在搜索的时候,把这些感染源压入队列中,然后进行宽搜。
#include <bits/stdc++.h>
using namespace std;
int n, m, a, b;
bool vis[510][510];
int num[510][510];
int dx[4] = {
-1, 1, 0, 0}, dy[4] = {
0, 0, -1, 1};
struct Node
{
int x, y;
};
queue<Node> q;
int main()
{
cin >> n >> m >> a >> b;
for (int i = 0; i < a; i++)
{
int x, y; cin >> x >> y;
q.push(Node{
x, y,});
vis[x][y] = 1;
}
while (!q.empty())
{
Node cur = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && !vis[tx][ty])
{
q.push(Node{
tx, ty});
vis[tx][ty] = 1;
num[tx][ty] = num[cur.x][cur.y] + 1;
}
}
}
for (int i = 0; i < b; i++)
{
int x, y; cin >> x >> y;
cout << num[x][y] << endl;
}
return 0;
}
4. [USACO19JAN]Icy Perimeter S
题目链接:https://www.luogu.com.cn/problem/P5198
题意:给你一个n×n的图,如果‘#’是挨到一起的(指它的东南西北),那么认为它们是一个整体,让你求这些不同的整体的最大面和最大周长,如果面积相同,找周长最小的那个。
思路:首先我们知道,一个方格的面积是1,周长是4,如果有重叠,面积只需要累加,而两个连到一起,周长就会减少2,如果上下左右都有,那么周长就会减4,那么我们就可以针对某一个‘#‘来看,遍历它的四个方向,如果其中某一个位置不存在一个’#‘,那么周长就加一。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
char mp[1010][1010];
bool vis[1010][1010];
int max_s, min_c, s, c;
int dx[4] = {
-1, 1, 0, 0}, dy[4] = {
0, 0, -1, 1};
void dfs(int x, int y)
{
if (vis[x][y]) return ;
vis[x][y] = 1;
s ++;
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > n || mp[tx][ty] == '.') c ++;
if (mp[tx][ty] == '#') dfs(tx, ty);
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (mp[i][j] == '#' && !vis[i][j])
{
s = 0, c = 0;
dfs(i, j);
if (s > max_s) max_s = s, min_c = c;
else if (s == max_s) min_c = min(min_c, c);
}
}
cout << max_s << ' ' << min_c << endl;
return 0;
}
5.单词接龙
题目链接:https://www.luogu.com.cn/problem/P1019
题意:给你n个字符串,让你找到它们能接成的最长的字符串,求其长度。每个字符串最多能使用两次,每个字符串之间是不能完全包含的。并且给出了你起始的字母。
思路:最先看到这个题的时候没得啥子思路,后面想了一下,你要连接成一个最长的字符出最先开始,你首先肯定要选两个来连接,那我们可以先把所有可以连接的两个字符串来接起来,并且记录它们的长度,并且都是第一个接到第二个后面,这跟我们后面的连接也并不冲突,所以我们用一个二维的数组连表示连接第i个字符串和第j个字符串组成的新的字符串,原字符串的长度,意思就是减去重合部分。
int cal(int x, int y)
{
bool flag = true;
int idx_y = 0;
for (int i = str[x].size() - 1; i >= 0; i--)
{
for (int j = i; j < str[x].size(); j++)
{
if (str[x][j] != str[y][idx_y ++])
{
flag = false;
break;
}
}
if (flag == true)
return str[x].size() - i;
idx_y = 0;
flag = true;
}
return 0;
}
经过上面的步骤,我们得到了连接任意两个字符串可以得到的长度。然后题目中给你了起始的字母,这个时候我们需要循环找到起始字母是给定的那个字母,以它开始进行dfs,在dfs中,有三种情况是不满足的,1.完全包含关系。2.连接这两个的形成的新字符串的长度为0。3.某个字符串使用的次数超过两次。后面的就是回溯dfs了。
void dfs(int x)
{
bool flag = false;
for (int i = 1; i <= n; i++)
{
if (vis[i] >= 2) continue;
if (overlap[x][i] == 0) continue;
if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;
temp += str[i].size() - overlap[x][i];
vis[i] ++;
flag = true;
dfs(i);
temp -= str[i].size() - overlap[x][i];
vis[i] --;
}
if (flag == false) ans = max(ans, temp);
return ;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
int n;
string str[30];
int overlap[30][30], vis[30];
char ch;
int ans = -1;
int temp = 0;
int cal(int x, int y)
{
bool flag = true;
int idx_y = 0;
for (int i = str[x].size() - 1; i >= 0; i--)
{
for (int j = i; j < str[x].size(); j++)
{
if (str[x][j] != str[y][idx_y ++])
{
flag = false;
break;
}
}
if (flag == true)
return str[x].size() - i;
idx_y = 0;
flag = true;
}
return 0;
}
void dfs(int x)
{
bool flag = false;
for (int i = 1; i <= n; i++)
{
if (vis[i] >= 2) continue;
if (overlap[x][i] == 0) continue;
if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;
temp += str[i].size() - overlap[x][i];
vis[i] ++;
flag = true;
dfs(i);
temp -= str[i].size() - overlap[x][i];
vis[i] --;
}
if (flag == false) ans = max(ans, temp);
return ;
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> str[i];
cin >> ch;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
overlap[i][j] = cal(i, j);
for (int i = 1; i <= n; i++)
{
if (str[i][0] == ch)
{
vis[i] ++;
temp = str[i].size();
dfs(i);
vis[i] = 0;
}
}
cout << ans << endl;
return 0;
}
6.CF510B Fox And Two Dots
题目链接:https://www.luogu.com.cn/problem/CF510B
题意:给你一个n×m的棋盘,然后棋盘上有不同颜色的点,问你是否存在一个环,这个环的颜色相同。
思路:我们想一下,怎样才能构成一个点,首先颜色相同,其次是,我们在搜索的时候,最后一个点肯定是起点,只有满足上面两个点,才说明能够构成一个环。
那么我们在搜索的时候,对于每一个点,我们需要确定,此时位置的点的坐标和这个点是由那个点变过来的,因为我们在bfs的时候,可能会存在这样的情况,就是新访问的这个点,可能是上一个点的上一个点,显然这种情况我们应该继续搜素。所以对于每一个点和bfs我们就该这么写:
struct Node
{
int x, y;
int lx, ly;
};
void bfs(int x, int y)
{
q.push({
x, y, -1, -1});
while (!q.empty())
{
Node cur = q.front(); q.pop();
vis[cur.x][cur.y] = 1;
for (int i = 0; i < 4; i++)
{
int tx = cur.x + dx[i], ty = cur.y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == cur.lx && ty == cur.ly)) continue;
if (mp[cur.x][cur.y] != mp[tx][ty]) continue;
if (vis[tx][ty])
{
flag = true;
break;
}
else q.push({
tx, ty, cur.x, cur.y});
}
if (flag) break