Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 11566 | Accepted: 3010 |
Description
dog.gopher gopher.rat rat.tiger aloha.aloha arachnid.dog
A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example,
aloha.aloha.arachnid.dog.gopher.rat.tiger
Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.
Input
Output
Sample Input
2 6 aloha arachnid dog gopher rat tiger 3 oak maple elm
Sample Output
aloha.arachnid.dog.gopher.rat.tiger ***
Source
題意:當一個單詞的尾字母和另一個單詞的首字母相同的時候,則這兩個單詞可以連接起來。給出一系列的單詞,問最終能否連成一排,若可以連成一排,則輸出其中字母序最小的那個。
分析:歐拉路、歐拉回路系列問題。該題和POJ1386不同的是需要輸出歐拉路徑並且在存在多條的時候需要輸出歐拉路徑最小的。所以此時需要事先對字符串排個序。考慮到該題需要歐拉路徑,所以可以使用鄰接表的形式對邊進行處理。數組實現鄰接表的方法可參見:http://blog.csdn.net/qq_26071477/article/details/52599363。對於字符串可以將字符串看成一條邊,首字母指向尾字母。該題在字母序最小方面的處理,可以在存儲邊的時候選出最小的起點或者終點,如果最終能生成歐拉回路的時候,就從這一點出發輸出字符串。但如果最終生成的是歐拉路,則必須是從出度比入度大一的那一點出發輸出歐拉路。該題是有向圖判斷歐拉路、歐拉回路的方法在此不贅述。最終dfs生成結果數組即可,注意由於遞歸的特性,在輸出的時候需要注意從後向前輸出。代碼思路參見:http://www.cnblogs.com/kuangbin/p/3537544.html 。代碼中有詳細註釋,在此給出:
// 判斷單詞是否連成一排的
#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=1005;
//該題使用鄰接表進行遍歷
struct edge
{
int to,next;//next表示該邊的前一條邊 以u爲起點
int id;
int vis;//表示這條邊有沒有訪問過
} e[maxn];
string s[maxn];
int adj[27];//以i爲起點的最後一條邊的編號
int in[27],out[27];//出度 入度數組
int res[maxn];
int cnt,num;
int dfs(int s)
{
for(int i=adj[s]; i!=-1; i=e[i].next)//遍歷鄰接表 從而遍歷數組
if(!e[i].vis)//保證每條邊只走一次 生成歐拉路
{
e[i].vis=1;
dfs(e[i].to);
res[num++]=e[i].id;//結果數組中保存字符串數組的下標
}
}
int cmp(string sa,string sb)//大的邊排在前面
{
return sa>sb;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
//初始化
cnt=0,num=0;
memset(adj,-1,sizeof(adj));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
//輸入
for(int i=0; i<n; i++)
cin>>s[i];
sort(s,s+n,cmp);//根據鄰接表添加邊的特性 每次後面的同一個起點的比都是添加在最前面
int start=28;//字母序在0-25
for(int i=0; i<n; i++)//添邊
{
int u=s[i][0]-'a',v=s[i][s[i].length()-1]-'a';
out[u]++;
in[v]++;
e[cnt].to=v;//該邊指向v
e[cnt].next=adj[u];//上一條邊
e[cnt].id=i;
e[cnt].vis=0;
adj[u]=cnt++;//越在前的邊其字母序越大
start=min(start,min(u,v));//記錄最小的起點或者終點 以其作爲最終生成的路徑的起點 此處考慮到 是歐拉回路的情況
}
int c1=0,c2=0,c0=0,flag=1;
for(int i=0; i<26; i++)
{
if(in[i]-out[i]==1)//入度比出度大1的點的個數
c2++;
else if(in[i]-out[i]==-1)//出度比入度大1的點的個數
{
c1++;//此時是歐拉路 必須從出度比入度大1 的點出發
start=i;//如果是歐拉路的話 則需要從出度比入度大一的點出發
}
else if(in[i]==out[i])
c0++;
else
{
flag=0;//如果存在 差值在0 +-1之外的情況則不可能構成歐拉路或者歐拉回路
break;
}
}
if(!flag||!((c1==0&&c2==0)||(c1==1&&c2==1)))
{
printf("***\n");
continue;
}
dfs(start);//在此使用dfs判連通 也可以使用並查集判連通
//如果使用並查集判連通的話 則還需要dfs生成路徑
//使用dfs判連通的話可以直接根據返回的num的值判斷圖是否連通
if(num!=n)//圖不連通
{
printf("***\n");
continue;
}
for(int i=num-1; i>=0; i--)//由於遞歸的特性 生成的小的值在數組最後
{
cout<<s[res[i]];
if(i)
printf(".");//輸出需注意
else
printf("\n");
}
}
}
歐拉路系列算是小結,以後遇到再溫習。
特記下,以備後日回顧。