[bzoj1138][BFS][DP]Baj 最短回文路

123 篇文章 1 订阅
17 篇文章 0 订阅

Description

N个点用M条有向边连接,每条边标有一个小写字母。 对于一个长度为D的顶点序列,回答每对相邻顶点Si到Si+1的最短回文路径。
如果没有,输出-1。 如果有,输出最短长度以及这个字符串。

Input

第一行正整数N和M ( 2 ≤ N ≤ 400 , 1 ≤ M ≤ 60,000 )
接下来M行描述边的起点,终点,字母。接下来D表示询问序列长度 ( 2 ≤ D ≤ 100 ) 再接下来D个1到N的整数

Output

对于D-1对相邻点,按要求输出一行。如果没合法方案,输出-1。 如果有合法,输出最短长度

Sample Input

6 7

1 2 a

1 3 x

1 4 b

2 6 l

3 5 y

4 5 z

6 5 a

3

1 5 3

Sample Output

3

-1

题解


普通的bfs
考虑f[i][j]表示从i点到j点的最短回文串
大力bfs就会TLE
我们加一个g[i][j][k]表示 从i~j,除了最后一条边外都是回文串且最后一条边的权值为K
发现f和g会互相转移
再大力bfs…
就可以了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
int mp[410][410][30];
struct edge{int x,y,c,next;}a[590005],b[590005];int len1,last1[410],len2,last2[410][30];
void ins1(int x,int y,int c){len1++;a[len1].x=x;a[len1].y=y;a[len1].c=c;a[len1].next=last1[x];last1[x]=len1;}
void ins2(int x,int y,int c){len2++;b[len2].x=x;b[len2].y=y;b[len2].next=last2[x][c];last2[x][c]=len2;}
int g[410][410][30],f[410][410];
int n,m;char ch[10];
struct node
{
	int x,y,pa;
	node(){}
	node(int _x,int _y,int _pa){x=_x;y=_y;pa=_pa;}
}tmp;
queue<node> q;
int main()
{
//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();scanf("%s",ch+1);
		mp[x][y][ch[1]-'a'+1]=1;
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=26;k++)if(mp[i][j][k])ins1(i,j,k),ins2(j,i,k);
	memset(f,63,sizeof(f));memset(g,63,sizeof(g));
	for(int i=1;i<=n;i++)f[i][i]=0,q.push(node(i,i,0));
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j)
		for(int k=1;k<=26;k++)if(mp[i][j][k])f[i][j]=1,q.push(node(i,j,0));
	while(!q.empty())
	{
		tmp=q.front();q.pop();
		int x=tmp.x,y=tmp.y;
		if(!tmp.pa)
		{
			for(int k=last1[y];k;k=a[k].next)
			{
				int i=a[k].y,cal=a[k].c;
					if(g[x][i][cal]>f[x][y]+1)g[x][i][cal]=f[x][y]+1,q.push(node(x,i,cal));
			}
		}
		else
		{
			for(int k=last2[x][tmp.pa];k;k=b[k].next)
			{
				int i=b[k].y;
				if(f[i][y]>g[x][y][tmp.pa]+1)f[i][y]=g[x][y][tmp.pa]+1,q.push(node(i,y,0));
			}
		}
	}
	int D=read();
	int la=read();D--;
	while(D--)
	{
		int x=read();
		if(f[la][x]>=1e9)puts("-1");
		else printf("%d\n",f[la][x]);
		la=x;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值