Luogu P6937 [ICPC2017 WF]Secret Chamber at Mount Rushmore
(从洛谷博客搬过来的)
简要题意:告诉你可以从哪些字符转化为哪些字符,然后再问你某一个字符串能否转化为另一个字符串。
这里提供两种做法:爆搜和传递闭包。
算法一 爆搜
看到数据范围后给孩子乐坏了,这不爆搜就能直接过吗?!
然后就爆系统栈了。
仔细想了一下,应该是出现了环导致死递归了,就比如这种情况:
a -> b
b -> c
c -> a
所以这样就寄了。
要解决也不难,直接开一个 数组,然后访问到某个点时就标记为 ,这样就不会在环里死递归了。
然后记得每次递归之前都要 一遍。
代码:
#include <iostream>
#include <map>
#include <cstring>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define N 35
#define M 505
#define LENGTH 55
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
/*
*/
int m, Q, star_cnt, lena, lenb;
char vis[N];
char st[LENGTH], ed[LENGTH];
int head[N];
struct star{int v, nxt;};
struct star e[M<<1];
map<char, int> mp;
inline void star_add(int u, int v){e[++ star_cnt].v=v, e[star_cnt].nxt=head[u], head[u]=star_cnt;}
char dfs(int x, int faer, int target){
vis[x] = true;
if (x == target)
return true;
for (re i = head[x] ; i ; i = e[i].nxt){
int v = e[i].v;
if (v == faer or vis[v] == true)
continue;
if (dfs(v, x, target) == true)
return true;
}
return false;
}
inline void work(){
scanf("%d %d", &m, &Q);
for (re i = 1 ; i <= 26 ; ++ i)
mp['a'+i-1] = i;
char uu, space, vv, enter; uu = getchar();
for (re i = 1 ; i <= m ; ++ i){
uu = getchar(), space = getchar(), vv = getchar(), enter = getchar();
star_add(mp[uu], mp[vv]);
}
while (Q --){
scanf("%s %s", st+1, ed+1);
lena = strlen(st+1), lenb = strlen(ed+1);
if (lena != lenb)
{cout << "no" << '\n'; continue;}
for (re i = 1 ; i <= lena ; ++ i){
memset(vis, false, sizeof(vis)); vis[mp[st[i]]] = true;
if (dfs(mp[st[i]], 0, mp[ed[i]]) == false)
{cout << "no" << '\n'; goto Continuer;}
}
cout << "yes" << '\n';
Continuer:{}
}
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a, a);
#endif
// Fastio_setup();
work();
return GMY;
}
算法二 传递闭包
因为字母只有 个,我们开数组不会太大,所以每次 所耗时间几乎可以不计。但是当这题并不是字母而数据范围更大时,这种方法就会 掉。就比如:
给定 个元素,他们有 种关系,其中每一个关系都形如一个二元组 ,表示元素 可以转化为元素 。你需要回答 次询问,每次询问都形如一个二元组 ,即询问元素 是否能转化为 。
数据范围:,,。
这时候如果你还是像刚才那样对于每个询问都挨个搜一遍,时间复杂度是 的,极限数据下显然会 掉。
那么我们考虑预处理出每个点之间的关系,然后 回答询问。
考虑使用传递闭包。 各位应该学过,传递闭包和 很像,也是枚举中间转移点。时间复杂度是 的。
最后记得初始化 can[i][i] = true
。一个元素从自己变成自己当然是可以的。
代码:
#include <iostream>
#include <cctype>
#include <cstring>
#include <map>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define N 35
#define M 505
#define LENGTH 55
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
/*
*/
int n, m, Q, lena, lenb;
char st[LENGTH], ed[LENGTH];
char can[N][N];
map<char, int> mp;
inline void Floyd(){
for (re k = 1 ; k <= n ; ++ k)
for (re i = 1 ; i <= n ; ++ i)
for (re j = 1 ; j <= n ; ++ j)
can[i][j] |= (can[i][k] & can[k][j]);
}
inline void work(){
cin >> m >> Q; n = 26;
for (re i = 1 ; i <= 26 ; ++ i)
mp[(char)('a'+i-1)] = i, can[i][i] = true;
char uu, vv;
for (re i = 1 ; i <= m ; ++ i){
cin >> uu >> vv;
can[mp[uu]][mp[vv]] = true;
}
Floyd();
while (Q --){
cin >> st+1 >> ed+1; lena = strlen(st+1), lenb = strlen(ed+1);
if (lena != lenb)
{puts("no"); goto Continuer;}
for (re i = 1 ; i <= lena ; ++ i)
if (can[mp[st[i]]][mp[ed[i]]] == false)
{puts("no"); goto Continuer;}
puts("yes");
Continuer:{}
}
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a, a);
#endif
Fastio_setup();
work();
return GMY;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现