题目大意
有n个祭坛,每个祭坛有东西南北四个方向
每个祭坛可以选择一个方向发出射线,保证射线不能相交以及射线不能经过其他祭坛
抽象的来说,如果我们以东方向为x轴,北方向为y轴,建立笛卡尔坐标系,那么每个祭坛将会对应一个整点。每个点向上下左右四个方向之一连出一条射线,这些射线不能相交且射线不能经过除了发出点之外的其他点。
求有多少种方法选择每个祭坛的方向,使得祭坛间不发生冲突
题目解析
首先如果一个点往某个方向有另一个点,那么这个点显然不能往这个方向发出射线
我们按照x坐标递增的顺序处理点,发现其实只需要记录4个东西就可以DP了
1、往上的点的y坐标最小值
2、往下的点的y坐标最大值
3、往右的点的y坐标最大值
4、往右的点的y坐标最小值
转移很容易实现,注意如果DP值本身为0就不用转移
代码
#include<bits/stdc++.h>
#define N 60
#define M 998244353
using namespace std;
struct A
{
int x,y;
}e[N];
int n,flag,x,y,ans,f[2][N][N][N][N];
bool cmp(A a,A b)
{
return a.x==b.x?a.y<b.y:a.x<b.x;
}
int min(int x,int y)
{
if(!x) return y;
if(!y) return x;
return e[x].y<e[y].y?x:y;
}
int max(int x,int y)
{
if(!x) return y;
if(!y) return x;
return e[x].y>e[y].y?x:y;
}
bool check(int x,int y,int k)
{
if (y==1&&e[k].x==e[x].x&&e[k].y>e[x].y) return 0;
if (y==2&&e[k].x==e[x].x&&e[k].y<e[x].y) return 0;
if (y==3&&e[k].y==e[x].y&&e[k].x<e[x].x) return 0;
if (y==4&&e[k].y==e[x].y&&e[k].x>e[x].x) return 0;
return 1;
}
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
cin>>e[i].x>>e[i].y;
sort(e+1,e+n+1,cmp);
f[0][0][0][0][0]=y=1,x=0;
for(int i=1;i<=n;i++)
{
for (int j=1;j<=4;j++)
{
flag=0;
for(int k=1;k<=n;k++)
if(!check(i,j,k))
{
flag=1;
break;
}
if(!flag)
for(int u=0;u<i;u++)
for(int d=0;d<i;d++)
for(int l=0;l<i;l++)
for(int r=0;r<i;r++)
if(f[x][u][d][l][r])
{
if(j==1&&(e[u].y<e[i].y||!u)) (f[y][u][d][min(i,l)][r]+=f[x][u][d][l][r])%=M;
if(j==2&&(e[d].y>e[i].y||!d)) (f[y][u][d][l][max(r,i)]+=f[x][u][d][l][r])%=M;
if(j==3&&((e[l].y>e[i].y&&e[r].y<e[i].y)||(!l&&!r)||(e[l].y>e[i].y&&!r)||(e[r].y<e[i].y&&!l))) (f[y][u][d][l][r]+=f[x][u][d][l][r])%=M;
if(j==4) (f[y][max(i,u)][min(i,d)][l][r]+=f[x][u][d][l][r])%=M;
}
}
x^=1,y^=1;
memset(f[y],0,sizeof(f[y]));
}
for(int u=0;u<=n;u++)
for(int d=0;d<=n;d++)
for(int l=0;l<=n;l++)
for(int r=0;r<=n;r++)
(ans+=f[x][u][d][l][r])%=M;
cout<<ans;
}