题意:链接略
方法: DP
解析:
显然我们可以找回文中点然后宽搜向两边拓展。
不过这样的复杂度的话…
枚举中点再拓展,岂不有点爆炸?
所以我们换个方式来优化。
我们设F[i][j]表示由i到j的最短回文路径长度。
设G[i][j][k]表示从i到j走一条回文路径再走一个小写字母k的最短回文路径长度。
有了这个辅助式子,问题就变得简单多了。
所有的状态最多也就n^2*k个。
直接上宽搜即可。
F[i][j]=min{G[z][j][k]+1&&edge[i][z]==k}
G[i][j][k]=min{F[i][w]+1&&edge[w][j]==k}
代码:
#include<queue>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define N 440
#define M 60100
#define K 30
using namespace std;
int n,m;
int l,r;
int map[N][N];
int f[N][N];
int g[N][N][K];
int head[N],head2[N];
int cnt;
struct node
{
int from,to,next;
}edge[M<<1];
struct element
{
int from,to,alpha;
}q[N*N*K];
void init()
{
memset(head,-1,sizeof(head));
memset(head2,-1,sizeof(head2));
cnt=1;
}
void edgeadd(int from,int to)
{
edge[cnt].from=from,edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
edge[cnt].from=to,edge[cnt].to=from;
edge[cnt].next=head2[to];
head2[to]=cnt++;
}
void bfs()
{
while(l!=r+1)
{
element u=q[l++];
int x=u.from,y=u.to,alpha=u.alpha;
if(!u.alpha)
{
for(int i=head[y];i!=-1;i=edge[i].next)
{
int w=edge[i].to;
if(f[x][y]+1<g[x][w][map[y][w]])
{
g[x][w][map[y][w]]=f[x][y]+1;
q[++r]=(element){x,w,map[y][w]};
}
}
}else
{
for(int i=head2[x];i!=-1;i=edge[i].next)
{
int w=edge[i].to;
if(g[x][y][map[w][x]]+1<f[w][y])
{
f[w][y]=g[x][y][map[w][x]]+1;
q[++r]=(element){w,y,0};
}
}
}
}
}
int s[N];
int main()
{
l=1,r=0;
scanf("%d%d",&n,&m);
init();
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
for(int i=1;i<=m;i++)
{
int x,y;
char tmp[2];
scanf("%d%d",&x,&y);
scanf("%s",tmp);
map[x][y]=tmp[0]-‘a‘+1;
edgeadd(x,y);
f[x][y]=1;
q[++r]=(element){x,y,0};
}
for(int i=1;i<=n;i++)f[i][i]=0,q[++r]=(element){i,i,0};
bfs();
int d;
scanf("%d",&d);
for(int i=1;i<=d;i++)scanf("%d",&s[i]);
for(int i=2;i<=d;i++)
{
if(f[s[i-1]][s[i]]!=0x3f3f3f3f)
printf("%d\n",f[s[i-1]][s[i]]);
else puts("-1");
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-12-18 08:17:29