UVa10129 例题 6-16 单词(Play On Words)

原题链接: UVa-10129

题目大意:

  输入n(n<=100000)个单词,是否可以把所有这些单词排成一个序列,使得每个单词头尾相连(只有两个单词对应的头尾相等时才能相连)。每个单词最多包含1000个小写字母。输入中可以有重复的单词。

解题思路:

 可以根据紫书后面提供的思路进行解决。将单词的首尾字母当做点,每个单词当做边。然后这道题就变成了一个图是否存在欧拉通路。要解决欧拉通路问题有两种方法一种是DFS,另一种是利用并查集。我用的是前一种方法(因为并查集我并不会┑( ̄Д  ̄)┍)。


 思路也是和紫书上的一样的,先判断底图(把有向图变成无向图)是否连通,然后再判断所有点的出度和入度是否相同(欧拉回路),或者只有两个点的出入度不相同,而且正好是出入度之差分别为1或者-1(有向图的欧拉通路)。如果都满足则说明存在欧拉通路(回路),门也就能打开了。

遇到问题:

 一开始写DFS判断是否连通的时候,突然脑袋一抽,差点和BFS弄混了(真的很容混啊!_( :з」∠)_)。


代码:

#include<iostream>
#include<string>
#include<cstring>
#include<set>

using namespace std;

void connected(int v);
bool degree_to_meet();
void init();

const int MAXN = 26 + 4;
//二维数组g用来存边,    in[i]来存i的出度,out同理,   cnt用来在DFS之后判断是否访问了所有的点,也就是图是否连通 
int g[MAXN][MAXN], in[MAXN], out[MAXN], cnt;
set<int> chr;		//用来存所有的点 
bool vis[MAXN];		//DFS时用来判断是否访问过i 

int main()
{
	//freopen("input.txt","r",stdin); 
	//freopen("output.txt","w",stdout); 
	int n, m;
	string s;
	cin >> n;
	while (n--)
	{
		cin >> m;
		while (m--)							//输入初始化 
		{
			cin >> s;
			int ch_in = s[0] - 'a', ch_out = s[s.size()-1] - 'a';
			g[ch_in][ch_out] = 1;g[ch_out][ch_in] = 1;
			in[ch_in]++; out[ch_out]++;
			chr.insert(ch_in); chr.insert(ch_out);
		}
		cnt = 1;
		connected(*chr.begin());		//进行DFS 
		if (cnt == chr.size() && degree_to_meet()) //判断两个条件是否满足 
			cout << "Ordering is possible.\n";
		else cout << "The door cannot be opened.\n";
		init();
	}
	return 0;
}

bool degree_to_meet()				//判断是否满足欧拉通路(回路)出度和入度的关系 
{
	bool ch_begin = false, ch_end = false;
	for (set<int>::iterator it = chr.begin(); it != chr.end(); it++)
	{
		int dv = in[*it] - out[*it];
		if (dv)
		{	//如果存在出入度之差不为1或-1,或者多个出入度为1或-1的点,则说明不是欧拉通路
			if ((dv != 1 && dv != -1) || (ch_begin && dv == 1) || (ch_end && dv == -1))
				return false;
			if (dv == 1) ch_begin = true;
			if (dv == -1) ch_end = true;
		}
	}
	return true;
}

void connected(int v)			//进行DFS 
{
	vis[v] = true;
	for (set<int>::iterator it = chr.begin(); it != chr.end(); it++)
	{
		if (g[v][*it] && !vis[*it])
		{
			cnt++;				//每次访问一个新的点都要计数,以便以后判断是否所有点都被访问 
			connected(*it);
		}
	}
	return ;
}

void init()			//初始化所有数组和set 
{
	chr.clear();
	memset(g, 0, sizeof(g));
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	memset(vis, 0, sizeof(vis));
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值