题意:
在一个n*m的四联通网格中有一个凸的联通块。当一个联通块与每行每列的交都是线段时,我们称它是凸的。对于两个点x和y,J(x,y)表示从x到y最少拐几个弯。询问max(J(x,y))。
n,m<=2000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 2100
using namespace std;
struct node{
int x,y;
friend bool operator ==(node x,node y)
{
return (x.x==y.x)&&(x.y==y.y);
}
}a[2*N],b[2*N];
char s[N][N];
int n,m,l[N],r[N],u[N],d[N],al,bl,ans;
int mrd(node a,node b)
{
int x1=a.x,y1=a.y,x2=b.x,y2=b.y;
if(x1==x2)
{
for(int i=y1;i<=y2;i++) if(d[i]>x1) return 1;
return 0;
}
else
{
for(int i=x1;i<=x2;i++) if(r[i]>y1) return 1;
return 0;
}
}
void rd(int x,int y)
{
al=1;a[1]=(node){x,y};
while(1)
{
x=a[al].x;y=a[al].y;
if(al%2) {if(r[x]==y) return;a[++al]=(node){x,r[x]};}
else {if(d[y]==x) return;a[++al]=(node){d[y],y};}
}
}
void dr(int x,int y)
{
bl=1;b[1]=(node){x,y};
while(1)
{
x=b[bl].x;y=b[bl].y;
if(bl%2==0) {if(r[x]==y) return;b[++bl]=(node){x,r[x]};}
else {if(d[y]==x) return;b[++bl]=(node){d[y],y};}
}
}
void make_rd(int x,int y)
{
int t=0;
rd(x,y);dr(x,y);
if(al==1 || bl==1)
{
if(al==1 && bl==1) t=0;
else if(al==1) t=bl-2+mrd(b[bl-1],b[bl]);
else t=al-2+mrd(a[al-1],a[al]);
}
else if(a[al]==b[bl])
{
if(al==bl) t=al-2;
else
{
if(al>bl) t=bl-2+mrd(a[al-1],a[al]);
else t=al-2+mrd(b[bl-1],b[bl]);
}
}
else
{
int t1=al-2,t2=bl-2,p=al+1;
for(int i=2;i<=al && i-1<=bl;i++)
if(a[i].x<b[i-1].x || a[i].y<b[i-1].y) {p=i;break;}
if(p<al) t1--;
else if(p>al)
{
if(a[al].y==a[al-1].y) t1+=mrd((node){b[al-1].x+1,a[al].y},a[al]);
else t1+=mrd((node){a[al].x,b[al-1].y+1},a[al]);
}
p=bl+1;
for(int i=2;i<=bl && i-1<=al;i++)
if(b[i].x<a[i-1].x || b[i].y<a[i-1].y) {p=i;break;}
if(p<bl) t2--;
else if(p>bl)
{
if(b[bl].y==b[bl-1].y) t2+=mrd((node){a[bl-1].x+1,b[bl].y},b[bl]);
else t2+=mrd((node){b[bl].x,a[bl-1].y+1},b[bl]);
}
t=max(t1,t2);
}
ans=max(ans,t);
}
int mru(node a,node b)
{
int x1=a.x,y1=a.y,x2=b.x,y2=b.y;
if(x1==x2)
{
for(int i=y1;i<=y2;i++) if(u[i]<x1) return 1;
return 0;
}
else
{
for(int i=x2;i<=x1;i++) if(r[i]>y1) return 1;
return 0;
}
}
void ru(int x,int y)
{
al=1;a[1]=(node){x,y};
while(1)
{
x=a[al].x;y=a[al].y;
if(al%2) {if(r[x]==y) return;a[++al]=(node){x,r[x]};}
else {if(u[y]==x) return;a[++al]=(node){u[y],y};}
}
}
void ur(int x,int y)
{
bl=1;b[1]=(node){x,y};
while(1)
{
x=b[bl].x;y=b[bl].y;
if(bl%2==0) {if(r[x]==y) return;b[++bl]=(node){x,r[x]};}
else {if(u[y]==x) return;b[++bl]=(node){u[y],y};}
}
}
void make_ru(int x,int y)
{
int t=0;
ru(x,y);ur(x,y);
if(al==1 || bl==1)
{
if(al==1 && bl==1) t=0;
else if(al==1) t=bl-2+mru(b[bl-1],b[bl]);
else t=al-2+mru(a[al-1],a[al]);
}
else if(a[al]==b[bl])
{
if(al==bl) t=al-2;
else
{
if(al>bl) t=bl-2+mru(a[al-1],a[al]);
else t=al-2+mru(b[bl-1],b[bl]);
}
}
else
{
int t1=al-2,t2=bl-2,p=al+1;
for(int i=2;i<=al && i-1<=bl;i++)
if(a[i].x>b[i-1].x || a[i].y<b[i-1].y) {p=i;break;}
if(p<al) t1--;
else if(p>al)
{
if(a[al].y==a[al-1].y) t1+=mru((node){b[al-1].x-1,a[al].y},a[al]);
else t1+=mru((node){a[al].x,b[al-1].y+1},a[al]);
}
p=bl+1;
for(int i=2;i<=bl && i-1<=al;i++)
if(b[i].x>a[i-1].x || b[i].y<a[i-1].y) {p=i;break;}
if(p<bl) t2--;
else if(p>bl)
{
if(b[bl].y==b[bl-1].y) t2+=mru((node){a[bl-1].x-1,b[bl].y},b[bl]);
else t2+=mru((node){b[bl].x,a[bl-1].y+1},b[bl]);
}
t=max(t1,t2);
}
ans=max(ans,t);
}
int main()
{
freopen("jinxiety.in","r",stdin);
freopen("jinxiety.out","w",stdout);
int z=0;
while(1)
{
z++;
scanf("%d%d",&n,&m);
if(n==0) break;
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
/*if(z==3112)
{
for(int i=1;i<=n;i++) printf("%s\n",s[i]+1);
return 0;
}*/
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]=='.') continue;
if(l[i]==0) l[i]=j;
r[i]=j;
}
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(s[j][i]=='.') continue;
if(u[i]==0) u[i]=j;
d[i]=j;
}
ans=0;
for(int i=1;i<=n;i++)
{
if(l[i]==0) continue;
make_rd(i,l[i]);
make_ru(i,l[i]);
int oo=1;
}
//if(z<=2)
printf("%d\n",ans);
for(int i=1;i<=n;i++) l[i]=r[i]=0;
for(int i=1;i<=m;i++) u[i]=d[i]=0;
}
return 0;
}
题解:
显然可以只考虑右上和右下两种走法,并以每行的左端点为起点。
对于右下的情况,核心的idea就是
考虑一直只走顶点,这样对于线段两侧的点,即非顶点的点,可以通过一次拐弯走到,所以只有最后一条线段会影响答案,特判即可。
对于一个出发点的两条路径,如果某个时刻开始不再交错,就可以如上调整让答案变优。
特判超多。。
O(nm)