洛谷 P7380 [COCI 2018/2019 #6] Konj 题解
P7380 [COCI 2018/2019 #6] Konj 题解
题意
给定 \(n\) 条线段两端端点坐标,保证一定与 \(x\) 轴或 \(y\) 轴平行。求最小的矩阵能表示出从点 \(T\) 出发到达的所有点。
如果两条线段相交或间接可以走到则称这两条线段相连。
思路
注意到虽然 \(n\) 十分之大,但端点坐标却在 \(0\sim 300\) 之间,所以可以考虑维护点。
不难想到对于每条线段,都在图上打上标记,表示这条线段的存在。最后再从点 \(T\) 出发,搜索搜一下就可以得到答案矩阵。
即对于每个点 \((x,y)\),若有至少一条线段经过,则 vis[x][y]=1。最后搜索时就从点 \(T\) 遍历四连通块即可。
但这样显然是不行的。因为如果两条线段相邻但不相交,但搜索时会被判为联通。
hack 如下:
...*
...*
####
其中 # 和 * 代表不同的两条线段。且两条线段没有交点。
那么如何优化?
注意到四个方向并不多,可以对于每个点判断四个方向可不可以走。
显然如果在一条线段内,那么是可以按照这条线段的走向走的,即有两个方向可以走。
显然如果是线段的端点,则只有一个方向可以走,特判一下就行啦。
最后因为题目中的 \(y\) 坐标是按照平面直角坐标系来的,所以输出的时候要注意一下。
因为题目还要求说是最小的矩阵,所以在搜索中维护一下 \(x\) 坐标和 \(y\) 坐标的最大最小值即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=305,nxt[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int n,Tx,Ty,maxx,maxy,minx=305,miny=305;
bool g[M][M][5];//1 up 2 down 3 left 4 right
bool vis[M][M];
struct L{
int x1,y1,x2,y2;
}l[N];
void addg(int i)
{
int x1=l[i].x1,x2=l[i].x2,y1=l[i].y1,y2=l[i].y2;
if(x1==x2)// // y
{
if(y1<y2)swap(y1,y2);//方便起见
g[x1][y1][2]=1;g[x1][y2][1]=1;
for(int y=y2+1;y<y1;++y)
g[x1][y][1]=g[x1][y][2]=1;//竖着走的线段可以上下两边
return;
}
// // x
if(x1>x2)swap(x1,x2);//方便
g[x1][y1][4]=g[x2][y1][3]=1;
for(int x=x1+1;x<x2;++x)
g[x][y1][3]=g[x][y1][4]=1; //横着走的线段可以左右两边
return;
}
void dfs(int x,int y)
{
vis[x][y]=1;
maxx=max(maxx,x);minx=min(minx,x);
maxy=max(maxy,y);miny=min(miny,y);
for(int i=0;i<4;++i)
{
int tx=x+nxt[i][0],ty=y+nxt[i][1];
if(tx<0||tx>300||ty<0||ty>300||vis[tx][ty])continue;
if(!i)//up
if(!g[x][y][1])continue;
if(i==1)//right
if(!g[x][y][4])continue;
if(i==2)//left
if(!g[x][y][3])continue;
if(i==3)//down
if(!g[x][y][2])continue;
dfs(tx,ty);
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;++i)
cin>>l[i].x1>>l[i].y1>>l[i].x2>>l[i].y2;
cin>>Tx>>Ty;
for(int i=1;i<=n;++i)
addg(i);
dfs(Tx,Ty);
for(int y=maxy;y>=miny;--y)//注意啦!!是按照平面直角坐标系的y坐标
{
for(int x=minx;x<=maxx;++x)
cout<<(vis[x][y]?'#':'.');
cout<<'\n';
}
return 0;
}

浙公网安备 33010602011771号