AcWing 175. 电路维修
AcWing 175. 电路维修
题目链接
175. 电路维修
达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。
翰翰的家里有一辆飞行车。
有一天飞行车的电路板突然出现了故障,导致无法启动。
电路板的整体结构是一个 \(R\) 行 \(C\) 列的网格(\(R,C≤500\)),如下图所示。
每个格点都是电线的接点,每个格子都包含一个电子元件。
电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。
在旋转之后,它就可以连接另一条对角线的两个接点。
电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。
达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。
她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。
不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。
注意:只能走斜向的线段,水平和竖直线段不能走。
输入格式
输入文件包含多组测试数据。
第一行包含一个整数 \(T\),表示测试数据的数目。
对于每组测试数据,第一行包含正整数 \(R\) 和 \(C\),表示电路板的行数和列数。
之后 4R$ 行,每行 \(C\) 个字符,字符是"/"
和"\"
中的一个,表示标准件的方向。
输出格式
对于每组测试数据,在单独的一行输出一个正整数,表示所需的最小旋转次数。
如果无论怎样都不能使得电源和发动机之间连通,输出 NO SOLUTION
。
数据范围
\(1≤R,C≤500\),
\(1≤T≤5\)
输入样例:
1
3 5
\\/\\
\\///
/\\\\
输出样例:
1
样例解释
样例的输入对应于题目描述中的情况。
只需要按照下面的方式旋转标准件,就可以使得电源和发动机之间连通。
解题思路
01bfs
把每一个横竖交点作为结点,当对角线与电缆重合时,权值为 \(0\),否则为 \(1\)(即旋转电缆),转化为从左上角到右小角的最小权值,可利用双端队列的bfs
,权值为 \(0\) 则进队首,否则进队尾,即每次优先扩展权值小的,遍历到终点时可保证最短。另外,需要注意如何用一个值表示一个坐标保证值不重复
- 时间复杂度:\(O(T\times R\times C)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=300000;
int t,n,m,d[N];
char g[505][505];
vector<pair<int,int>> adj[N];
bool vis[N];
void bfs()
{
memset(d,0x3f,sizeof d);
memset(vis,0,sizeof vis);
deque<int> q;
q.push_back(0);
d[0]=0;
while(q.size())
{
int x=q.front();
q.pop_front();
if(x==n*(m+1)+m)break;
if(vis[x])continue;
vis[x]=true;
for(auto [y,w]:adj[x])
{
if(d[y]>d[x]+w)
{
d[y]=d[x]+w;
if(w)q.push_back(y);
else
q.push_front(y);
}
}
}
}
int main()
{
for(scanf("%d",&t);t;t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%s",g[i]);
for(int i=0;i<N;i++)adj[i].clear();
//对角线
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
adj[i*(m+1)+j].emplace_back((i+1)*(m+1)+j+1,g[i][j]!='\\'),adj[(i+1)*(m+1)+j+1].emplace_back(i*(m+1)+j,g[i][j]!='\\');
//反对角线
for(int i=0;i<n;i++)
for(int j=1;j<=m;j++)
adj[i*(m+1)+j].emplace_back((i+1)*(m+1)+j-1,g[i][j-1]!='/'),adj[(i+1)*(m+1)+j-1].emplace_back(i*(m+1)+j,g[i][j-1]!='/');
if((n+m)&1)puts("NO SOLUTION");
else
{
bfs();
printf("%d\n",d[n*(m+1)+m]);
}
}
return 0;
}
2019. 拖拉机
题目链接
AcWing 2019. 拖拉机
干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了。
他的奶牛非常调皮,决定对约翰来场恶作剧。
她们在田地的不同地方放了 \(N\) 捆干草,这样一来,约翰想要开走拖拉机就必须先移除一些干草捆。
拖拉机的位置以及 \(N\) 捆干草的位置都是二维平面上的整数坐标点。
拖拉机的初始位置上没有干草捆。
当约翰驾驶拖拉机时,他只能沿平行于坐标轴的方向(北,南,东和西)移动拖拉机,并且拖拉机必须每次移动整数距离。
例如,驾驶拖拉机先向北移动 \(2\) 单位长度,然后向东移动 \(3\) 单位长度。
拖拉机无法移动到干草捆占据的位置。
请帮助约翰确定他需要移除的干草捆的最小数量,以便他能够将拖拉机开到二维平面的原点。
输入格式
第一行包含三个整数:\(N\) 以及拖拉机的初始位置 \((x,y)\)。
接下来 \(N\) 行,每行包含一个干草捆的位置坐标 \((x,y)\)。
输出格式
输出约翰需要移除的干草捆的最小数量。
数据范围
\(1≤N≤50000\),
\(1≤x,y≤1000\)
输入样例:
7 6 3
6 2
5 2
4 3
2 1
7 3
5 4
6 4
输出样例:
1
解题思路
01bfs
这里不是边权而是点权,同样可以按照01bfs
处理,从起点开始bfs
,如果遇到干草捆,则点权为 \(1\),否则为 \(0\),即转换为从起点到终点的最短路,注意这里可以到达边界后再从边界到达终点,所以实际边界要扩展一个长度
- 时间复杂度:\(O(4\times 1000^2)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int d[N][N],sx,sy,dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
bool v[N][N],st[N][N];
void bfs()
{
memset(d,0x3f,sizeof d);
memset(v,0,sizeof v);
d[sx][sy]=0;
deque<pair<int,int>> q;
q.push_front({sx,sy});
while(q.size())
{
auto [x,y]=q.front();
q.pop_front();
if(x==1&&y==1)break;
if(v[x][y])continue;
v[x][y]=true;
for(int i=0;i<4;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(nx<0||nx>1001||ny<0||ny>1001)continue;
if(d[nx][ny]>d[x][y]+st[nx][ny])
{
d[nx][ny]=d[x][y]+st[nx][ny];
if(st[nx][ny])
q.push_back({nx,ny});
else
q.push_front({nx,ny});
}
}
}
}
int main()
{
int x,y,n;
scanf("%d%d%d",&n,&sx,&sy);
while(n--)
{
scanf("%d%d",&x,&y);
st[x][y]=true;
}
bfs();
printf("%d",d[1][1]);
return 0;
}