[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。 如果有合法,输出最短长度

输入示例

  a
x
b
l
y
z
a

输出示例

-

数据规模及约定

见“输入

题解

朴素的 dp 是这样的:设 f[i][j] 表示节点 i 到节点 j 的最短回文长度,转移的时候枚举字母 x,设节点 j 沿着带有字母 x 的边走一格所能到达的点集为集合 A,设 i 逆着带有字母 x 的边走一格所能达到的点集为集合 B。那么对于 ∀a ∈ A, ∀b ∈ B,就可以更新 f[a][b] 了,即 f[a][b] = min{ f[a][b], f[i][j] + 2 }。

但是我们发现这个算法被爆菊了。。。就是如果有很多条相同字母的边指向 i,相同字母的边从 j 出发,转移就会被卡成 O(n2) 的了。。。

解决方式就是添加一个中间状态 g[i][j][c],表示节点 i 到节点 j 的路径最后一个字母是 c,并且除掉最后一个字母后它就是一个回文路径,在这样的情况下的最短长度。那么对于状态 f[i][j],可以用节点 j 沿着字母 c(枚举)走一格到达的所有节点 b 来更新 g[i][b][c];对于状态 g[i][b][c],可以用节点 i 逆着字母 c(这个字母是固定的,即状态中的字母)走一格到达的所有节点 a 来更新 f[a][b],这样状态数变成了原来的 27 倍,转移复杂度变成了 O(n)。

转移顺序比较乱,可以用 BFS 帮助转移。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 410
#define maxa 26
#define maxm 10660 bool G[maxn][maxn][maxa];
int f[maxn][maxn], g[maxn][maxn][maxa];
vector <int> from[maxn][maxa], to[maxn][maxa];
void AddEdge(int a, int b, int c) {
to[a][c].push_back(b);
from[b][c].push_back(a);
return ;
} struct Pair {
int a, b, c;
Pair() {}
Pair(int _1, int _2, int _3): a(_1), b(_2), c(_3) {}
};
queue <Pair> Q; int main() {
int n = read(), m = read();
for(int i = 1; i <= m; i++) {
int a = read(), b = read(); char ch[2]; scanf("%s", ch);
G[a][b][ch[0]-'a'] = 1;
} memset(f, -1, sizeof(f)); memset(g, -1, sizeof(g));
for(int i = 1; i <= n; i++) Q.push(Pair(i, i, -1)), f[i][i] = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) if(i != j) {
bool fl = 0;
for(int c = 0; c < maxa; c++) if(G[i][j][c]) {
AddEdge(i, j, c);
fl = 1; break;
}
if(fl) f[i][j] = 1, Q.push(Pair(i, j, -1));
}
while(!Q.empty()) {
Pair u = Q.front(); Q.pop();
int a = u.a, b = u.b, c = u.c, tmp = c < 0 ? f[a][b] : g[a][b][c];
// printf("(%d, %d, %d)\n", a, b, c);
if(c < 0)
for(c = 0; c < maxa; c++)
for(int i = 0; i < to[b][c].size(); i++) {
int B = to[b][c][i];
if(g[a][B][c] < 0) g[a][B][c] = tmp + 1, Q.push(Pair(a, B, c));
}
else
for(int i = 0; i < from[a][c].size(); i++) {
int A = from[a][c][i];
if(f[A][b] < 0) f[A][b] = tmp + 1, Q.push(Pair(A, b, -1));
}
} int q = read(), st = read();
for(int i = 2; i <= q; i++) {
int u = read();
printf("%d\n", f[st][u]);
st = u;
} return 0;
}

[BZOJ1138][POI2009]Baj 最短回文路的更多相关文章

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

    1138: [POI2009]Baj 最短回文路 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 161  Solved: 48[Submit][Sta ...

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

    额,,貌似网上的题解都说超时之类的. 然而我这个辣鸡在做的时候不知道在想什么,连超时的都不会. 超时的大概是这样的,f[x][y]表示x到y的最短回文路,然后更新的话就是 f[x][y]更新到 f[a ...

  3. [Swift]LeetCode214. 最短回文串 | Shortest Palindrome

    Given a string s, you are allowed to convert it to a palindrome by adding characters in front of it. ...

  4. [python,2019-02-15] 最短回文串

    给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出: "aaa ...

  5. leetcode 214. 最短回文串 解题报告

    给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出: "aaa ...

  6. 214 Shortest Palindrome 最短回文串

    给一个字符串 S, 你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串.例如:给出 "aacecaaa",返回 "aaacecaaa ...

  7. Leetcode 214.最短回文串

    最短回文串 给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出: &qu ...

  8. Java实现 LeetCode 214 最短回文串

    214. 最短回文串 给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出 ...

  9. [LeetCode] Shortest Palindrome 最短回文串

    Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. ...

随机推荐

  1. Service官方教程(10)Bound Service的生命周期函数

    Managing the Lifecycle of a Bound Service When a service is unbound from all clients, the Android sy ...

  2. 加密解密(3)Bob到CA申请证书过程

    网络安全中最知名的人物大概就是Bob和Alice了,因为很多安全原理阐述中都用这两个虚拟人物来进行实例说明. 我们来看看Bob是怎么从CA中心获得一个数字证书的: 1.Bob首先创建他自己的密钥对(k ...

  3. poj2573Bridge(过桥问题)

    链接 A,B为最快和次快 有两种方式可以使c,d过桥 一是a与c一起走,a回来接d再与d一起走,一直到对岸人为0为止 而是 a与b一起走 a回来送灯 c与d一起走 b回来送灯 重复此过程. 只剩2人时 ...

  4. 窗体WINFORM

    窗体的事件:删除事件:先将事件页面里面的挂好的事件删除,再删后台代码里面的事件 Panel是一个容器 1.Label -- 文本显示工具Text:显示的文字取值.赋值:lable1.Text 2.Te ...

  5. 初学Ajax

    AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX = 异步 JavaScript和 ...

  6. 死磕 java集合之LinkedList源码分析

    问题 (1)LinkedList只是一个List吗? (2)LinkedList还有其它什么特性吗? (3)LinkedList为啥经常拿出来跟ArrayList比较? (4)我为什么把LinkedL ...

  7. Android组件化开发(注意事项)

    1.Manifest合并 在Android studio编译项目时,无论你使用了几个Module都会把所有Manifest最终合并成一个,需要我们注意的是application标签下这个几个属性引用的 ...

  8. 新浪qq登录

    一:到腾讯QQ互联上申请APPID和APPKEY.申请地址: http://connect.qq.com/ 如同,这里我们可以获取到需要跳转到的APPID和APPKEY.新浪微博的申请同理 二:在Th ...

  9. C/C++ new/delete []、内存泄漏、动态数组

    一.概念 new/delete是用于动态分配和撤销内存的运算符.new/delete是c++里才有的,c中是用malloc和free,c++虽然也可以用,但是不建议用.当我们使用关键字new在堆上动态 ...

  10. 迅为I.MX6Q开发板配不同分辨率不同尺寸液晶屏幕

    I.MX6Q开发板: 核心板参数 尺寸:51mm*61mm iMX6Q四核CPU:Freescale Cortex-A9 四核 i.MX6Q,主频 1.2 GHz iMX6DL双核CPU:Freesc ...