Luogu P6937 [ICPC2017 WF]Secret Chamber at Mount Rushmore

(从洛谷博客搬过来的)


简要题意:告诉你可以从哪些字符转化为哪些字符,然后再问你某一个字符串能否转化为另一个字符串。

这里提供两种做法:爆搜和传递闭包。

算法一 爆搜

看到数据范围后给孩子乐坏了,这不爆搜就能直接过吗?!

然后就爆系统栈了。

仔细想了一下,应该是出现了环导致死递归了,就比如这种情况:

a -> b
b -> c
c -> a

所以这样就寄了。

要解决也不难,直接开一个 vis 数组,然后访问到某个点时就标记为 true,这样就不会在环里死递归了。

然后记得每次递归之前都要 memset 一遍。

代码:

#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;
}

算法二 传递闭包

因为字母只有 26 个,我们开数组不会太大,所以每次 memset 所耗时间几乎可以不计。但是当这题并不是字母而数据范围更大时,这种方法就会 T 掉。就比如:

给定 n 个元素,他们有 m 种关系,其中每一个关系都形如一个二元组 (i,j),表示元素 i 可以转化为元素 j。你需要回答 Q 次询问,每次询问都形如一个二元组 (x,y),即询问元素 x 是否能转化为 y

数据范围:n300m10000Q100000

这时候如果你还是像刚才那样对于每个询问都挨个搜一遍,时间复杂度是 O(Qm) 的,极限数据下显然会 T 掉。

那么我们考虑预处理出每个点之间的关系,然后 O(1) 回答询问。

考虑使用传递闭包。Floyd 各位应该学过,传递闭包和 Floyd 很像,也是枚举中间转移点。时间复杂度是 O(n3) 的。

最后记得初始化 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;
}
posted @   char_phi  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示