BZOJ 1138 [POI2009]Baj 最短回文路 DP

题意:链接略

方法: 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

BZOJ 1138 [POI2009]Baj 最短回文路 DP的相关文章

bzoj 1138: [POI2009]Baj 最短回文路 dp优化

1138: [POI2009]Baj 最短回文路 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 161  Solved: 48[Submit][Status] Description N个点用M条有向边连接,每条边标有一个小写字母. 对于一个长度为D的顶点序列,回答每对相邻顶点Si到Si+1的最短回文路径. 如果没有,输出-1. 如果有,输出最短长度以及这个字符串. Input 第一行正整数N和M ( 2 ≤ N ≤ 400 , 1 ≤ M ≤

BZOJ 1138 POI2009 Baj 最短回文路 BFS

+题目大意:给定一张有向图,每个点有一个字符,多次求两点的最短回文路 据说这道题第一次做的人都会T? 一开始的思路是这样的:令fx,y表示从点x走到点y的最短回文路径,转移fx,y=min{fz,w+2|x?c?>z,w?c?>y} 然后广搜,果断T了= = 冗余的转移太多了-- 正解是这样的: 令gx,y,c表示从点x走到点y,除了最后一条边之外是回文路径且最后一条边的字符为c的最短路 然后转移是这样的: gx,y,c=min{fx,z+1|z?c?>y} fx,y=min{gz,y,

bzoj 1138: [POI2009]Baj 最短回文路

额,,貌似网上的题解都说超时之类的. 然而我这个辣鸡在做的时候不知道在想什么,连超时的都不会. 超时的大概是这样的,f[x][y]表示x到y的最短回文路,然后更新的话就是 f[x][y]更新到 f[a][b] 当x->a,y->b且边的颜色是一样的. 然后yy了一下为什么会超时呢.... 然后想到了一个sb的情况..两个菊花图连起来...这样的复杂度就呵呵呵了..每次用f[x][y]更新的话都要枚举一个颜色,然后把另一边的颜色找出来,这样显然是要爆炸的. 为了避免这种sb情况,就引入了一个叫题

[BZOJ1138][POI2009]Baj 最短回文路

试题描述 N个点用M条有向边连接,每条边标有一个小写字母. 对于一个长度为D的顶点序列,回答每对相邻顶点Si到Si+1的最短回文路径. 如果没有,输出-1. 如果有,输出最短长度以及这个字符串. 输入 第一行正整数N和M ( 2 ≤ N ≤ 400 , 1 ≤ M ≤ 60,000 ) 接下来M行描述边的起点,终点,字母.接下来D表示询问序列长度 ( 2 ≤ D ≤ 100 ) 再接下来D个1到N的整数 输出 对于D-1对相邻点,按要求输出一行.如果没合法方案,输出-1. 如果有合法,输出最短长

bzoj 2565: 最长双回文串

Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同).输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串. Input 一行由小写英文字母组成的字符串S. Output 一行一个整数,表示最长双回文子串的长度. Sample Input baacaabbacabb Sample Output 12 HINT

BZOJ 3676 【APIO2014】 回文串

题目链接:回文串 我终于也会回文自动机辣! 其实吗--我觉得回文自动机(听说这玩意儿叫\(PAM\))还是比较\(simple\)的--至少比\(SAM\)友善多了-- 所谓回文自动机,每个节点就代表一个回文串.回文自动机的每个节点有两个东西,一个是\(next\),一个是\(fail\).\(next_{u,x}\)指向节点\(u\)所代表的回文串在两端各添加一个字符\(x\)得到一个新的回文串.\(fail_u\)则指向\(u\)这个节点的最长后缀回文串(不包括本身).当然还有一个\(len

BZOJ 2565 最长双回文串 Hash+二分

题目大意:给定一个字符串,求一个最长的子串,该字串可以分解为两个回文子串 傻逼的我又忘了Manacher怎么写了= = 无奈Hash+二分吧 首先将字符串用分隔符倍增,然后求出以每个点为中心的最长回文半径 然后考虑两个回文串怎么合并成一个 我们发现图中以i为中心的回文串和以j为中心的回文串合并后长度恰好为(j-i)*2 能合并的前提是以两个点为中心的回文串有交点 那么对于每个j我们要求出有交点的最左侧的i 维护一个后缀min随便搞搞就可以了 #include <cstdio> #include

[BZOJ]4755: [Jsoi2016]扭动的回文串

Time Limit: 10 Sec  Memory Limit: 512 MB Description JYY有两个长度均为N的字符串A和B. 一个"扭动字符串S(i,j,k)由A中的第i个字符到第j个字符组成的子串与B中的第j个字符到第k个字符组成的子串拼接而成. 比如,若A='XYZ',B='UVW',则扭动字符串S(1,2,3)='XYVW'. JYY定义一个"扭动的回文串"为如下情况中的一个: 1.A中的一个回文串: 2.B中的一个回文串: 3.或者某一个回文的扭动

bzoj2084/luoguP3501 [Poi2010]Antisymmetry(回文自动机+dp)

bzoj Luogu 对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作"反对称"字符串.比如00001111和010101就是反对称的,1001就不是. 现在给出一个长度为N的01字符串,求它有多少个子串是反对称的. 题解时间 这玩意咋看都像是回文串不是嘛. 然后与此同时还是经典计数问题. 所以考虑能不能以这里面的这个规则写个PAM 发现还真能搞: 首先这种串只有偶串,所以不建奇方向的(跳到奇根1直接跳出) 此外再把match改一下 完事了. #in