POJ2337 欧拉回路

题意:给你n个由小写字母组成的单词,要求将这n个单词连接起来,使得前一个单词的最后一个字母和后一个单词的第一个字母相容,输出字典序最小的解

思路:不难发现此题可以转化为欧拉路径问题,把每个字符串的第一个字母当作起点,最后一个字母当作终点,连一条有向边,求此图字典序最小的欧拉路径。

对于有向图来说,存在一条欧拉路径的充要条件为此图联通并且要么对于每个顶点都有出度等于入度,要么有且仅有一个顶点出度比入度少1,有且仅有一个顶点入度比出度少1,其余顶点出度等于入度。

对于联通性的判断来说,直接用dfs即可,只要能把所有的边都遍历到,此图就是联通的。当然也可以用并查集判断联通。

对于字典序最小的要求,只需把给定的所有字符串排序,按照字典序从小到大的顺序加边即可。

代码如下

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
string s[1005];
int in[26],out[26];
struct node
{
    int y,id,flag;
    node(int ty,int tid,int tflag):y(ty),id(tid),flag(tflag){}
    node(){}
};
vector<node> g[26];
int ans[1005];
int cnt=0;
void dfs(int now)
{
    for(int i=0;i<g[now].size();i++)
    {
        if(!g[now][i].flag)
        {
            g[now][i].flag=1;
            dfs(g[now][i].y);
            ans[++cnt]=g[now][i].id;
        }
    }
}
int main()
{
    ios_base::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--)
    {
        for(int i=0;i<26;i++)
            g[i].clear();
        memset(in,0,sizeof in);
        memset(out,0,sizeof out);
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>s[i];
        sort(s+1,s+1+n);
        int st=100;
        for(int i=1;i<=n;i++)
        {
            int a=s[i][0]-'a';
            int b=s[i][s[i].size()-1]-'a';
            st=min(st,a);
            g[a].push_back(node(b,i,0));
            out[a]++;in[b]++;
        }
        int d1=0,d2=0;
        for(int i=0;i<26;i++)
        {
            if(out[i]-in[i]==1)
            {
                d1++;
                st=i;
            }
            else if(in[i]-out[i]==1)
            {
                d2++;
            }
            else if(out[i]-in[i])
            {
                d1=100;
            }
        }
        if(!(d1==1&&d2==1||d1==0&&d2==0))
        {
            printf("***\n");
            continue;
        }
        cnt=0;
        dfs(st);
        if(cnt!=n)
        {
            printf("***\n");
            continue;
        }
        for(int i=cnt;i>=1;i--)
        {
            cout<<s[ans[i]];
            if(i==1) cout<<endl;
            else cout<<".";
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值