春季每日一题2022篇(三)
水桶传递队列(bfs + 技巧)
方法一 : BFS
解题思路
这题由于是从一个点对另一个点的最短距离。因此利用BFS每一次都只拓展一层的特性,当我们第一层搜索到终点的时候的距离就是最短距离。
- 湖和牛棚哪个当起点和终点都是一样的。
代码:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define x first
#define y second
typedef pair<int, int>PII;
PII s, e;
const int N = 12;
int n = 10, f[N][N];
char g[N][N];
int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
int bfs() {
queue<PII>q;
q.push(s);
while (q.size()) {
auto u = q.front();
q.pop();
for (int i = 0 ; i < 4 ; ++i) {
int xx = u.x + dx[i], yy = u.y + dy[i];
if (xx && xx <= n && yy && yy <= n && g[xx][yy] != 'R' && !f[xx][yy] ) {
f[xx][yy] = f[u.x][u.y] + 1;
if (xx == e.x && yy == e.y)
return f[xx][yy];
q.push({xx, yy});
}
}
}
return f[e.x][e.y];
}
int main() {
for (int i = 1 ; i <= n ; ++i)
cin >> (g[i] + 1);
for (int i = 1 ; i <= n ; ++i)
for (int j = 1; j <= n; ++j)
if (g[i][j] == 'B')
s = {i, j};
else if (g[i][j] == 'L')
e = {i, j};
cout << bfs() - 1 << endl;
return 0;
}
方法二 : 技巧 + 数学知识
解题思路
我们先不看岩石的作用的话,该题下两个点的最短距离应该是两个坐标之间的曼哈顿距离 - 1,
首先,如果湖和牛棚不在同一行也不再同列,那么不管岩石在哪里我们都能取到理论最小值。如图
那么什么时候岩石会影响到这个呢?答案就是当 湖和牛棚在同一行或者同一列的时候,且这个时候岩石出现在两者的连线之间,那么我们就需要多出两头牛来绕路了。
代码:
代码:
#include<bits/stdc++.h>
using namespace std;
char s[11];
int sx,sy,ex,ey,rx,ry;
int main()
{
for(int i=1;i<=10;i++){
cin>>s+1;
for(int j=1;j<=10;j++){
if(s[j]=='R')rx=i,ry=j;
else if(s[j]=='B')ex=i,ey=j;
else if(s[j]=='L')sx=i,sy=j;
}
}
int ans=abs(sx-ex)+abs(sy-ey)-1;
if((rx==sx&&sx==ex&&!(sy<ry^ry<ey))||(ry==sy&&sy==ey&&!(sx<rx^rx<ex)))ans+=2;
cout<<ans;
}
阻挡广告牌I (计算几何 ,矩形面积)
解题思路:
本题难点是如何快速求出,两个矩形至今相交的面积出来。如下图
两个矩形的水平边投影到 x 轴上的线段分别为
[
ax
1
,
ax
2
]
[\textit{ax}_1, \textit{ax}_2]
[ax1,ax2]和
[
bx
1
,
bx
2
]
[\textit{bx}_1, \textit{bx}_2]
[bx1,bx2]竖直边投影到 y 轴上的线段分别为
[
ay
1
,
ay
2
]
[\textit{ay}_1, \textit{ay}_2]
[ay1,ay2]和
[
by
1
,
by
2
]
[\textit{by}_1, \textit{by}_2]
[by1,by2]。如果两个矩形重叠,则重叠部分的水平边投影到 x 轴上的线段为
[
max
(
ax
1
,
bx
1
)
,
min
(
ax
2
,
bx
2
)
]
[\max(\textit{ax}_1, \textit{bx}_1), \min(\textit{ax}_2, \textit{bx}_2)]
[max(ax1,bx1),min(ax2,bx2)],竖直边投影到 y 轴上的线段为
[
max
(
ay
1
,
by
1
)
,
min
(
ay
2
,
by
2
)
]
[\max(\textit{ay}_1, \textit{by}_1), \min(\textit{ay}_2, \textit{by}_2)]
[max(ay1,by1),min(ay2,by2)],根据重叠部分的水平边投影到 x 轴上的线段长度和竖直边投影到 y 轴上的线段长度即可计算重叠部分的面积。只有当两条线段的长度都大于 0 时,重叠部分的面积才大于 0,否则重叠部分的面积为 0。
代码:
#include<iostream>
#include<cmath>
using namespace std;
#define x first
#define y second
typedef pair<int,int>PII;
struct node{
PII ld , rt;
}a ,b,c;
int main(){
cin>>a.ld.x>>a.ld.y>>a.rt.x>>a.rt.y;
cin>>b.ld.x>>b.ld.y>>b.rt.x>>b.rt.y;
cin>>c.ld.x>>c.ld.y>>c.rt.x>>c.rt.y;
PII lt = {c.ld.x , c.rt.y};
int ans = get_area(a) + get_area(b);
ans -= max(min(a.rt.x , c.rt.x) - max(a.ld.x,c.ld.x) , 0) * max(min(a.rt.y , c.rt.y) - max(a.ld.y,c.ld.y), 0);
ans -= max(min(b.rt.x , c.rt.x) - max(b.ld.x,c.ld.x) , 0) * max(min(b.rt.y , c.rt.y) - max(b.ld.y,c.ld.y), 0);
cout<<ans<<endl;
return 0;
}
阻挡广告牌 II
题目:题目链接
解题思路:
可以减少使用面积总共分为以下四种情况:
我们可以发现,如果是宽度大于,那么我们相交的矩形右上角和最下角的纵坐标与割草机的是一样的。同理如果是长度覆盖,则是x一样。因此我们借助上一题求出相交矩形左下角和右上角的坐标,如果满足上面说的,一个我们就用割草机广告的面积减去相交矩形的面积,这里如果完全覆盖结果为0符合题意,如果不相交得到割草机广告的面积也符合。其余就直接输出割草机广告的面积即可。
代码:
#include<iostream>
#include<cmath>
using namespace std;
#define x first
#define y second
typedef pair<int,int>PII;
struct ret{
PII ld , rt;
}a ,b;
int get(PII a ,PII b){
return (b.y - a.y) * ( b.x - a.x);
}
int main(){
cin>>a.ld.x>>a.ld.y>>a.rt.x>>a.rt.y;
cin>>b.ld.x>>b.ld.y>>b.rt.x>>b.rt.y;
int lx = max(a.ld.x , b.ld.x) , rx = min(a.rt.x , b.rt.x);
int dy = max(a.ld.y , b.ld.y) , uy = min(a.rt.y , b.rt.y);
if((lx == a.ld.x || rx == a.rt.x)&& uy == a.rt.y && dy == a.ld.y ) // 覆盖左右部分
cout<< get(a.ld ,a.rt) - max((rx - lx) , 0) * max((uy - dy),0)<<endl;
else if((dy == a.ld.y || uy == a.rt.y)&& rx == a.rt.x && lx == a.ld.x)
cout<<get(a.ld ,a.rt) - max((rx - lx) , 0) * max((uy - dy),0)<<endl;
else cout<<get(a.ld,a.rt)<<endl;
return 0;
}
传送
题目:题目链接
解题思路:
分类讨论,如果转送门在内部那么我们一定使用。其余有以下三种情况
这三种情况,转送门是可能对我们有用的。也就是我们需要花费一些距离去转送门之后获得的收益更多。我们只需要在判断情况后输出min即可。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int a ,b ,x,y;
int main(){
cin>>a>>b>>x>>y;
if(x > y)swap(x, y);
if(a > b)swap(a,b);
if(x>=a && y <= b)cout<< x - a + b - y<<endl;// 在内部
else if(x < a && y > b )cout<<min(b-a ,(a - x + y - b))<<endl; // 3
else if(x <= a && y>= a && y <= b )cout<<min(b-a , a - x + b - y)<<endl; //1
else if(y >= b && x >= a && x <= b)cout<<min(b-a , x - a + y - b)<<endl; // 2
else
cout<<b-a<<endl;
return 0;
}
组队井字游戏(模拟 + 技巧)
题目:题目
解题思路
- 我们要解决的是能快速辨别出,一行,一列,对角线中相同元素个数。
- 对于一对奶牛,我们在记录的时候不能重复。
- 对于第一个问题,我们利用set去重的特性,如果我们将三个元素插入set后大小变为1,说明有三个相同的元素,如果是2就是有两个相同元素。
- 对于第二个问题,我们以元素个数做下标,将该大小的set,压入set中。
代码
#include<iostream>
#include<set>
#include<vector>
#include<string>
using namespace std;
#define x first
#define y second
const int N = 4;
typedef set<char> SC;
typedef pair<int,int>PII;
string s[N];
set<SC>wo[N];
int n = 3;
void insert(vector<PII>cow)
{
SC co;
for(auto c : cow)
co.insert(s[c.x][c.y]);
wo[co.size()].insert(co); // 加入分配的牛
}
int main(){
for(int i = 0 ; i < n; ++i)cin>>s[i];
for(int i = 0 ; i < n ; ++i)insert({{i,0},{i,1},{i,2}}); // 行
for(int j = 0 ; j < n ; ++j)insert({{0,j},{1,j},{2,j}}); // 列
insert({{0,0},{1,1},{2,2}}); // 主对角线
insert({{0,2},{1,1},{2,0}}); // 副对角线
cout<<wo[1].size()<<endl<<wo[2].size()<<endl;
return 0;
}