题意
一个 r∗c 的矩阵中,有 n 个特殊点,每个特殊点有一个传送门(单向),共三种:
①.到达同一行的特殊点(横天门)
②.到达同一列的特殊点(纵寰门)
③.到达同一九宫格的特殊点(自由门)
求最多可以到达多少个特殊点
解法
Tarjan 缩点+拓扑排序+图上 DP :
和之前的考试的 teach 一题非常相似,不过这一题不能够使用 spfa ,而必须使用 DP
而为了消除后效性,所以可以按照拓扑序进行 DP
设 fu 表示到达 u 点(Tarjan 缩点之后的编号)能获得的最大点数,那么有:
fson=max(fson,fu+sizson) , son 为 u 的孩子,sizson 为 son 的联通块的点数
另外,本题的建图是一个重点:
容易想到,如果在第 i 个藏宝宫室可以传送到第j 个藏宝宫室,就连一条 i−>j 的有向边, Tarjan 强连通分量缩点之后,在新图上找出一条路径,使得这条路径上的所有点对应的强连通分量大小之和最大,这个可以按照拓扑序进行递推来实现。
但是,在建图上存在一个问题,从横天门或纵寰门引出的边可能特别多。在极端情况下, 105 个横天门在同一行里出现,这样时空复杂度都是无法承受的。考虑这一点进行优化:由于在同一行的横天门一定属于同一个强连通分量,所以在建边时,只需要对在同一行的横天门构建出一个环即可,不需要两两进行连边。而此行内的其他宫室,只需要从这个环中的任意一点向这个宫室连边即可。对于纵寰门也是一样。
此外,在建图的实现上有一些小技巧,具体见代码。
复杂度
O( knlogn ), k <script type="math/tex" id="MathJax-Element-23">k</script>为常数
70分spfa代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int MAXN=100010;
struct point
{
int x,y;
void gi() { scanf("%d%d",&x,&y); }
bool operator < (const point a) const
{
return x==a.x ? y<a.y : x<a.x;
}
}p[MAXN];
struct node
{
int next,to;
}t[MAXN*2],e[MAXN*12];
int head[MAXN],Head[MAXN],num;
int type[MAXN],siz[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],dis[MAXN];
int be[MAXN],s[MAXN];
int cnt,top,tot;
int n,r,c,ans;
int dx[9]={0,1,1,1,0,-1,-1,-1,0};
int dy[9]={0,1,0,-1,-1,-1,0,1,1};
vector<int> H[MAXN*10],L[MAXN*10];
vector<int> N[MAXN*10],M[MAXN*10];
map<int,int> id;
queue<int> q;
set<point> P;
void add(int u,int v)
{
if( u==v ) return ;
t[++num]=(node){ head[u],v };
head[u]=num;
}
void Add(int u,int v)
{
e[++num]=(node){ Head[u],v };
Head[u]=num;
}
void Tarjan(int k)
{
int x;
vis[k]=1,s[++top]=k;
dfn[k]=low[k]=++cnt;
for(int i=head[k]; i ;i=t[i].next)
{
x=t[i].to;
if( !dfn[x] )
Tarjan( x ),low[k]=min( low[k],low[x] );
else
if( vis[x] ) low[k]=min( low[k],dfn[x] );
}
if( dfn[k]==low[k] )
{
tot++;
do
{
x=s[top--],vis[x]=0;
be[x]=tot,siz[tot]++;
for(int i=head[x]; i ;i=t[i].next) if( !be[t[i].to] || be[t[i].to]!=tot ) Add( tot,t[i].to );
}while( x!=k );
}
}
void spfa(int s)
{
int tmp;
dis[s]=siz[s];
q.push( s );
while( !q.empty() )
{
tmp=q.front(),q.pop();
vis[tmp]=0;
for(int i=Head[tmp],x; i ;i=e[i].next)
{
x=be[e[i].to];
if( x==tmp ) continue ;
if( dis[x]<dis[tmp]+siz[x] )
{
dis[x]=dis[tmp]+siz[x];
if( !vis[x] ) vis[x]=1,q.push( x );
}
}
}
}
int main()
{
set<point>::iterator l;
int ex,ey;
scanf("%d%d%d",&n,&r,&c);
for(int i=1;i<=n;i++)
{
p[i].gi(),scanf("%d",&type[i]);
if( type[i]==1 ) H[p[i].x].push_back( i ),M[p[i].y].push_back( i );
if( type[i]==2 ) L[p[i].y].push_back( i ),N[p[i].x].push_back( i );
if( type[i]==3 ) N[p[i].x].push_back( i ),M[p[i].y].push_back( i );
id[c*(p[i].x-1)+p[i].y]=i;
P.insert( p[i] );
}
for(int i=1,x;i<=r;i++)
{
x=H[i].size();
if( x<2 ) continue ;
for(int j=1;j<=x-1;j++) add( H[i][j-1],H[i][j] );
add( H[i][x-1],H[i][0] );
}
for(int i=1,x;i<=c;i++)
{
x=L[i].size();
if( x<2 ) continue ;
for(int j=1;j<=x-1;j++) add( L[i][j-1],L[i][j] );
add( L[i][x-1],L[i][0] );
}
for(int i=1,x;i<=r;i++)
{
x=N[i].size()-1;
if( x<0 || !H[i].size() ) continue ;
for(int j=0;j<=x;j++) add( H[i][0],N[i][j] );
}
for(int i=1,x;i<=c;i++)
{
x=M[i].size()-1;
if( x<0 || !L[i].size() ) continue ;
for(int j=0;j<=x;j++) add( L[i][0],M[i][j] );
}
for(int i=1,x;i<=n;i++)
{
if( type[i]!=3 ) continue ;
for(int k=1;k<=8;k++)
{
ex=p[i].x+dx[k],ey=p[i].y+dy[k];
if( ex<1 || ex>r || ey<1 || ey>c ) continue ;
l=P.find( (point){ ex,ey } );
if( l==P.end() ) continue ;
add( i,id[c*(ex-1)+ey] );
}
}
num=0;
for(int i=1;i<=n;i++) if( !dfn[i] ) Tarjan( i );
for(int i=1;i<=tot;i++) vis[i]=0;
for(int i=1;i<=tot;i++) if( !dis[i] ) spfa( i );
for(int i=1;i<=tot;i++) ans=max( ans,dis[i] );
printf("%d\n",ans);
return 0;
}
100分DP代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int MAXN=100010;
struct point
{
int x,y;
void gi() { scanf("%d%d",&x,&y); }
bool operator < (const point a) const
{
return x==a.x ? y<a.y : x<a.x;
}
}p[MAXN];
struct node
{
int next,to;
}t[MAXN*2],e[MAXN*12];
int head[MAXN],Head[MAXN],num;
int dfnm[MAXN],deg[MAXN];
int type[MAXN],siz[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],dis[MAXN];
int be[MAXN],s[MAXN];
int cnt,top,tot;
int n,r,c,ans;
int dx[9]={0,1,1,1,0,-1,-1,-1,0};
int dy[9]={0,1,0,-1,-1,-1,0,1,1};
vector<int> H[MAXN*10],L[MAXN*10];
vector<int> N[MAXN*10],M[MAXN*10];
map<int,int> id;
queue<int> q;
set<point> P;
void add(int u,int v)
{
if( u==v ) return ;
t[++num]=(node){ head[u],v };
head[u]=num;
}
void Add(int u,int v)
{
e[++num]=(node){ Head[u],v };
Head[u]=num;
}
void Tarjan(int k)
{
int x;
vis[k]=1,s[++top]=k;
dfn[k]=low[k]=++cnt;
for(int i=head[k]; i ;i=t[i].next)
{
x=t[i].to;
if( !dfn[x] )
Tarjan( x ),low[k]=min( low[k],low[x] );
else
if( vis[x] ) low[k]=min( low[k],dfn[x] );
}
if( dfn[k]==low[k] )
{
tot++;
do
{
x=s[top--],vis[x]=0;
be[x]=tot,siz[tot]++;
for(int i=head[x]; i ;i=t[i].next) if( !be[t[i].to] || be[t[i].to]!=tot ) Add( tot,t[i].to );
}while( x!=k );
}
}
void topology()
{
int tmp;
for(int i=1;i<=tot;i++) if( !deg[i] ) q.push( i );
while( !q.empty() )
{
tmp=q.front(),q.pop();
dfn[tmp]=++cnt,dfnm[cnt]=tmp;
for(int i=Head[tmp],x; i ;i=e[i].next)
{
x=be[e[i].to],deg[x]--;
if( !deg[x] ) q.push( x );
}
}
}
int main()
{
set<point>::iterator l;
int ex,ey;
scanf("%d%d%d",&n,&r,&c);
for(int i=1;i<=n;i++)
{
p[i].gi(),scanf("%d",&type[i]);
if( type[i]==1 ) H[p[i].x].push_back( i ),M[p[i].y].push_back( i );
if( type[i]==2 ) L[p[i].y].push_back( i ),N[p[i].x].push_back( i );
if( type[i]==3 ) N[p[i].x].push_back( i ),M[p[i].y].push_back( i );
id[c*(p[i].x-1)+p[i].y]=i;
P.insert( p[i] );
}
for(int i=1,x;i<=r;i++)
{
x=H[i].size();
if( x<2 ) continue ;
for(int j=1;j<=x-1;j++) add( H[i][j-1],H[i][j] );
add( H[i][x-1],H[i][0] );
}
for(int i=1,x;i<=c;i++)
{
x=L[i].size();
if( x<2 ) continue ;
for(int j=1;j<=x-1;j++) add( L[i][j-1],L[i][j] );
add( L[i][x-1],L[i][0] );
}
for(int i=1,x;i<=r;i++)
{
x=N[i].size()-1;
if( x<0 || !H[i].size() ) continue ;
for(int j=0;j<=x;j++) add( H[i][0],N[i][j] );
}
for(int i=1,x;i<=c;i++)
{
x=M[i].size()-1;
if( x<0 || !L[i].size() ) continue ;
for(int j=0;j<=x;j++) add( L[i][0],M[i][j] );
}
for(int i=1;i<=n;i++)
{
if( type[i]!=3 ) continue ;
for(int k=1;k<=8;k++)
{
ex=p[i].x+dx[k],ey=p[i].y+dy[k];
if( ex<1 || ex>r || ey<1 || ey>c ) continue ;
l=P.find( (point){ ex,ey } );
if( l==P.end() ) continue ;
add( i,id[c*(ex-1)+ey] );
}
}
num=0;
for(int i=1;i<=n;i++) if( !dfn[i] ) Tarjan( i );
for(int i=1;i<=tot;i++) dfn[i]=0;
cnt=0;
for(int i=1;i<=tot;i++)
for(int j=Head[i]; j ;j=e[j].next) if( be[e[j].to]!=i ) deg[be[e[j].to]]++;
topology();
for(int i=1;i<=tot;i++) dis[i]=siz[i];
for(int i=1,x;i<=tot;i++)
{
x=dfnm[i];
for(int j=Head[x]; j ;j=e[j].next)
if( be[e[j].to]!=x )
dis[be[e[j].to]]=max( dis[be[e[j].to]],dis[x]+siz[be[e[j].to]] );
}
for(int i=1;i<=tot;i++) ans=max( ans,dis[i] );
printf("%d\n",ans);
return 0;
}