NYOJ 99 - 欧拉图 单词拼接

探讨如何通过构建有向图并运用欧拉路径算法解决寻找字典序最小的单词链问题,确保首尾字母相连,同时避免TLE。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Question:

描述

给你一些单词,请你判断能否把它们首尾串起来串成一串。

前一个单词的结尾应该与下一个单词的道字母相同。

aloha

dog

arachnid

gopher

tiger

rat

 

可以拼接成:aloha.arachnid.dog.gopher.rat.tiger

输入
第一行是一个整数N(0<N<20),表示测试数据的组数
每组测试数据的第一行是一个整数M,表示该组测试数据中有M(2<M<1000)个互不相同的单词,随后的M行,每行是一个长度不超过30的单词,单词全部由小写字母组成。
输出
如果存在拼接方案,请输出所有拼接方案中字典序最小的方案。(两个单词之间输出一个英文句号".")
如果不存在拼接方案,则输出
***
样例输入
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
样例输出
aloha.arachnid.dog.gopher.rat.tiger
***

2.Solution:

本题如果处理不当的话,非常容易造成TLE
首先,本题我们需要对题目进行抽象
我们将每个单词看作是一条边,单词的首字符和尾字符看作是边的起点和终点,这里我们就可以抽象出一个有26个顶点的稠密图,因为题目声明了图的稠密程度达到了1005:26所以说,在这里邻接矩阵的存储思路已经不再适用,我们需要利用邻接表来存储
并且,题目声明了搜索的顺序是边(和顶点)的字典序输出,所以本题我们需要利用strcmp函数来辅助构建有顺序的邻接表
PS:本题TLE很可能是因为没有考虑很多的特殊情况
1.入度与出度相差大不止1,造成TLE
2.不考虑回路的问题造成TLE
3.本日我给每条边加上了lazy标记,防止我搜索的时候进行重复搜索从而出现TLE或者WA的情况

有向图存在欧拉通路的充分必要条件:
有向图中只存在两个点的出度和入度相差位1,且相差情况刚好相反,其他点的入度和出度相等
并且欧拉通路存在之时,我们的通路的起点必定是入度小的那个点,终点必定是出度少的那个点

3.Code:

#include"iostream"
#include"cstdio"
#include"cstring"
#include"cstdlib"
#define N 30

using namespace std;

typedef struct node
{
	char data[32];
	struct node* next;
	bool lazy;
}point;

int t,n;
point head[30];
int in_degree[30];
int out_degree[30];
int begin,eend;
char s[1005][32];
int s_num=0;

void init()
{
	begin=eend=0;
	s_num=0;
	for(int i=0;i<30;i++)
	{
		head[i].next=NULL;
	}
	memset(in_degree,0,sizeof(in_degree));
	memset(out_degree,0,sizeof(out_degree));
	return ;
}

bool dfs(int root)
{
	if(s_num==n)
	{
		for(int i=0;i<n-1;i++)
		{
			printf("%s.",s[i]);
		}
		printf("%s\n",s[s_num-1]);
		return true;
	}
	point* k=head[root].next;
	while(k!=NULL)
	{
		if(k->lazy==1)
		{
			k=k->next;
			continue;
		}
		k->lazy=1;
		strcpy(s[s_num++],k->data);
		bool judge=dfs(k->data[strlen(k->data)-1]-'a');
		if(judge) return true;
		s_num--;
		k->lazy=0;
		k=k->next;
	}
	return false;
}

int main()
{
	char pp[32];
	scanf("%d",&t);getchar();
	while(t--)
	{
		init();
		scanf("%d",&n);getchar();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",pp);
			out_degree[pp[0]-'a']++;
			in_degree[pp[strlen(pp)-1]-'a']++;
			point* p=new point;
			strcpy(p->data,pp);
			p->next=NULL;
			p->lazy=0;
			
			point* w=&head[pp[0]-'a'];
			while(w->next!=NULL&&strcmp(w->next->data,pp)<0)
			{
				w=w->next;
			}
			p->next=w->next;
			w->next=p;
		}
		
		bool flag=1;
		int number=0;
		for(int i=0;i<30;i++)
		{
			if(in_degree[i]==out_degree[i]-1) begin=i;
			else if(in_degree[i]==out_degree[i]+1) eend=i;
			else
			{
				if(abs(in_degree[i]-out_degree[i])>=2) 
				{
					flag=0;
					printf("***\n");
					break;
				}
				number--;
			}
			number++;
		}
		if(flag==0) continue;
		if((number==0||number==2)&&dfs(begin));
		else printf("***\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值