poj 2337 Catenyms

题意:给你一些单词,请你判断能否把他们首尾串起来串成一串,前一个单词的结尾字母应该与下一个单词的首字母相同。并且输出所有方案中字典序最小的方案。


分析:这题实际上是求解欧拉回路,先根据欧拉回路的性质判断存不存在拼接方案,然后判断是欧拉回路还是欧拉通路,若是通路则只存在一个起点,则以那个点为起点进行dfs保存结果后输出,若是欧拉回路,则找到最小的字母为起点,然后进行dfs同样保存结果并输出。

有向图欧拉回路的判断:若一个图是连通的,并且所有顶点的入度等于出度,则存在欧拉回路,可以以任意点为起点再回到该点。

有向图的欧拉通路的判定:若一个图是连通的,并且除两个点外所有点的入度等于出度,而且那两个点,一个出度与入度之差为1,另一个出入度之差为-1,则存在欧拉通路。该通路一定是以出入之差为1的点为起点,然后回到出入度之差为-1的点。


代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string> 


using namespace std;

const int maxn = 35;
const int maxm = 1005;


struct UFset{
	int p[maxn];
	
	UFset(){}
	
	
	int Find(int x){return p[x] >= 0 ? p[x] = Find(p[x]) : x;}
	void Union(int r1, int r2){
			r1 = Find(r1);
			r2 = Find(r2);
			int t = p[r1] + p[r2];
			if(p[r1] < p[r2]){
				p[r2] = r1;
				p[r1] = t;
			}
			else{
				p[r1] = r2;
				p[r2] = t;
			}
	}
	void Clear(){
		for(int i = 0; i < maxn; i++)
			p[i] = -1;
	}
};


struct node{
	int v;
	int No;
	string word;
	
	node(int _v, int _No, string _word){
		v = _v; No = _No; word = _word;
	}
	
	bool operator < (const node &b) const{
		return word < b.word;
	}
};

int in[maxn];//入度 
int out[maxn];//出度 
int m;
char word[maxm][35];
vector<node> list[maxn];
bool flag[maxn];//'a'~'z'这些结点是否出现 
bool vis[maxm];//for dfs
UFset Set;
int ans[maxm];//结果路径 
int s;//出入度之差为1的点 
int t;//出入度之差为-1的点 
bool ok;//dfs是否找到了结果 


void input(){
	
	scanf("%d",&m);
	Set.Clear();
	for(int i = 0; i < maxn; i++) {
		list[i].clear(); flag[i] = false;
		in[i] = 0; out[i] = 0;
	}
	for(int i = 0; i < m; i++){
		scanf("%s",word[i]);
		int u = word[i][0] - 'a';
		int v = word[i][strlen(word[i])-1] - 'a';
		flag[u] = true; flag[v] = true;
		out[u]++; in[v]++;
		if(Set.Find(u) != Set.Find(v))
			Set.Union(u,v);
		list[u].push_back(node(v,i,word[i]));
		vis[i] = false;// 
	}
	//确保结果是字典序最小
	for(int i = 0; i < maxn; i++){
		sort(list[i].begin(),list[i].end());
	}
	
}


bool is_euler(){
	//判断是否是联通图
	int first = -1;
	for(int i = 0; i < maxn; i++){
		if(!flag[i]) continue;
		if(first == -1) first = i;
		if(Set.Find(first) != Set.Find(i))
			return false;
	}
	s = -1;
	t = -1;
	for(int i = 0; i < maxn; i++){
		if(!flag[i]) continue;
		if(in[i] == out[i]) continue;
		if(abs(out[i] - in[i]) > 1) return false;
		if(out[i]-in[i] == 1){
			if(s == -1) s = i;
			else return false;
		}
		if(out[i]-in[i] == -1){
			if(t == -1) t = i;
			else return false;
		}
	}
	return true;
}


void dfs(int s, int d){
	
	if(d == m){
		ok = true; return;
	}
	
	for(int i = 0; i < list[s].size(); i++){
		int v = list[s][i].v;
		int No = list[s][i].No;
		if(!vis[No]){
			vis[No] = true;
			ans[d] = No;
			dfs(v,d+1);
			vis[No] = false;
		}
		if(ok) return;
	}
}

void solve(){
	
	//如果是欧拉回路,则取字典序最小的点出发 
	if(s == -1){
		for(int i = 0; i < maxn; i++){
			if(!flag[i]) continue;
			s = i; break;
		}
	}
	ok = false;
	dfs(s,0);
	printf("%s",word[ans[0]]);
	for(int i = 1; i < m; i++){
		printf(".%s",word[ans[i]]);
	}
	puts("");
}

int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		input();
		if(is_euler()) solve();
		else puts("***");
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值