poj 2337 Catenyms(欧拉路+字典序+打印路径)

http://poj.org/problem?id=2337

题目大意:给出一组单词,如果两个单词,一个单词的头和另一个单词的尾相同,则可以相连,例如abce, efdg,可以相连,问这组单词能否排成一排,如果可以,求出字典序最小的那个。

思路:重点在建图。将单词的两个端点为顶点建有向边,用邻接表存储,用头插法建邻接表时,首先应对输入的字符串从大到小排序,这样才能保证按字典序输出。

首先判断图的连通性,然后判断是否欧拉路或欧拉通路。若是欧拉路,以出度大入度为1的点为起点,若是欧拉回路则以字典序最小的点为起点进行dfs。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <vector>
#define LL long long
#define _LL __int64
using namespace std;

struct node
{
    int u,v;
    int next;
    char str[22];
    int vis;
    bool operator < (const struct node &tmp)const
    {
    	if(strcmp(str,tmp.str) > 0)
			return true;
		return false;
    }
}s[1010];

int in[1010],out[1010]; //保存每个点的出入度
int cnt;//顶点个数
int ok[26];//ok[i] = 1 : i+'a'是顶点
int set[26];
int start,end;//记录欧拉路的起点与终点
int head[26];
char ans[1010][22];//保存输出结果
int c;

void init()
{
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(ok,0,sizeof(ok));
    cnt = 0;
    for(int i = 0; i < 26; i++)
        set[i] = i;
	memset(head,-1,sizeof(head));
	c = 0;
}

int find(int x)
{
    if(set[x] != x)
        set[x] = find(set[x]);
    return set[x];
}

//并查集判断是否图是否连通
bool check()
{
    int flag = 1;
    for(int i = 0; i < 26; i++)
    {
    	if(!ok[i]) continue;
        for(int j = i+1; j < 26; j++)
        {
        	if(!ok[j]) continue;
            if(find(i) != find(j))
            {
                flag = 0;
                break;
            }
        }
        if(!flag) break;
    }
    if(flag) return true;
    return false;
}

//判断是欧拉路还是欧拉回路
int judge()
{
    int c1 = 0,c2 = 0,c3 = 0;

    for(int i = 0; i < 26; i++)
    {
        if(ok[i])
        {
            if(in[i] == out[i])
                c3++;
            else if(in[i]-out[i] == 1)
			{
				end = i;
				c1++;
			}
            else if(out[i]-in[i] == 1)
			{
				start = i;
				c2++;
			}
        }
    }
	if(c3 == cnt)
		return 2; //欧拉回路
	else if(c1 == 1 && c2 == 1 && c3 == cnt-2) //欧拉路
		return 1;
	else
		return 0;
}

//dfs寻找欧拉路或欧拉回路,注意输出是逆序输出
void dfs(int u)
{
	for(int i = head[u]; i != -1; i = s[i].next)
	{
		if(!s[i].vis)
		{
			s[i].vis = 1;
			dfs(s[i].v);
			strcpy(ans[c++],s[i].str);
		}
	}
}

int main()
{
    int test,n,u,v;
    scanf("%d",&test);
    while(test--)
    {
        init();
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
		{
			scanf("%s",s[i].str);
			s[i].vis = 0;
		}
		sort(s,s+n); //对边从大到小排序
	
        for(int i = 0; i < n; i++)
        {
        	int len = strlen(s[i].str);

            u = s[i].str[0]-'a';
            v = s[i].str[len-1]-'a';

            if(!ok[u])
            {
                ok[u] = 1;
                cnt++;
            }
            if(!ok[v])
            {
                ok[v] = 1;
                cnt++;
            }
            //头插法建边
			s[i].u = u;
			s[i].v = v;
			s[i].next = head[u];
			head[u] = i;

            out[u]++;
            in[v]++;
            int uu = find(u);
            int vv = find(v);
            if(uu != vv)
                set[uu] = vv;
        }

        if(!check())
        {
            printf("***\n");
            continue;
        }

		int res = judge();

		if(!res)
		{
			printf("***\n");
            continue;
		}

		else if(res == 1) //欧拉路
		{
			dfs(start);
		}

		else //欧拉回路
		{
			for(int i = 0; i < 26; i++)
			{
				if(ok[i])
				{
					start = i;
					break;
				}
			}
			dfs(start);
		}

		printf("%s",ans[c-1]);
		for(int i = c-2; i >= 0; i--)
			printf(".%s",ans[i]);
		printf("\n");
    }
    return 0;
}


你可能感兴趣的:(欧拉回路)