poj2337

题意:给定一些单词,如果一个单词的尾字母与另一个的首字母相同则可以连接。问是否可以每个单词用一次,将所有单词连接,可以则输出字典序最小的序列。

分析:把每个字母作为一个结点。每个单词作为一条边。这样就可以把问题转化为欧拉路径。现判断欧拉路径是否存在。若存在则找到欧拉路径,找的方法是,先找到出度>入度(无欧拉回路)的结点,从起点开始,dfs(v,e),v是当前结点,e是到v的边。用vis数组记录某条边是否被访问过,从v开始继续搜索未访问过的边。每层dfs结束时把该层的边入栈。dfs整个结束后,把边依次出栈,得到的就是欧拉路径。

void dfs(int v, int e)
{
    vis[e] = true;
    for (int i = head[v]; i != -1; i = edge[i].next)
        if (!vis[i])
            dfs(edge[i].v, i);
    stk[top++] = e;
}

本题要求字典序最小,那么在dfs的时候就每次先访问字典序最小的边(原因很难说清),具体做法就是把边插入邻接表的时候冒泡一下,维护邻接表有序。当有欧拉回路时还要注意起点的选择,要使得路径的第一条边最小。

View Code
#include <iostream>
#include
<cstdio>
#include
<cstdlib>
#include
<cstring>
using namespace std;

#define maxn 30
#define maxl 25
#define maxm 1005

struct Edge
{
int v, next, id;
} edge[maxm];

int n, m;
char word[maxm][maxl];
int father[maxn];
int stk[maxm];
int in[maxn], out[maxn];
int head[maxn];
int ecount;
int top, s;
bool vis[maxm];
bool rep;

int getanc(int a)
{
if (a == father[a])
return a;
return father[a] = getanc(father[a]);
}

void merge(int a, int b)
{
father[getanc(a)]
= getanc(b);
}

void addedge(int a, int b, int c)
{
edge[ecount].next
= head[a];
edge[ecount].id
= c;
edge[ecount].v
= b;
head[a]
= ecount++;
int temp = head[a];
while (edge[temp].next != -1)
{
int u = edge[temp].id;
int v = edge[edge[temp].next].id;
if (strcmp(word[u], word[v]) > 0)
{
swap(edge[temp].v, edge[edge[temp].next].v);
swap(edge[temp].id, edge[edge[temp].next].id);
}
temp
= edge[temp].next;
}
}
void input()
{
for (int i = 0; i < n; i++)
father[i]
= i;
memset(
in, 0, sizeof(in));
memset(
out, 0, sizeof(out));
ecount
= 0;
memset(head,
-1, sizeof(head));
scanf(
"%d", &m);
for (int i = 0; i < m; i++)
{
scanf(
"%s", word[i]);
int a = word[i][0] - 'a';
int b = word[i][strlen(word[i]) - 1] - 'a';
addedge(a, b, i);
merge(a, b);
in[b]++;
out[a]++;
}
}

bool ok()
{
s
= 0;
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < n; i++)
{
if (abs(in[i] - out[i]) > 1)
return false;
if (in[i] - out[i] == 1)
cnt1
++;
if (out[i] - in[i] == 1)
{
s
= i;
cnt2
++;
}
}
if (cnt1 == 0 && cnt2 == 0)
rep
= true;
else
rep
= false;
if (cnt1 > 1 || cnt2 > 1 || cnt1 != cnt2)
return false;

cnt1
= 0;
for (int i = 0; i < n; i++)
if (i == father[i] && (in[i] | out[i]))
cnt1
++;
if (cnt1 > 1)
return false;

bool first = true;
if (rep)
for (int i = 0; i < n; i++)
if (head[i] != -1 && (first || strcmp(word[edge[head[s]].id], word[edge[head[i]].id]) > 0))
{
first
= false;
s
= i;
}
return true;
}

void dfs(int v, int e)
{
vis[e]
= true;
for (int i = head[v]; i != -1; i = edge[i].next)
if (!vis[i])
dfs(edge[i].v, i);
stk[top
++] = e;
}

void work()
{
memset(vis,
0, sizeof(vis));
top
= 0;
dfs(s, ecount);
}

void print()
{
top
--;
printf(
"%s", word[edge[stk[top - 1]].id]);
top
--;
while (top > 0)
{
printf(
".%s", word[edge[stk[top - 1]].id]);
top
--;
}
putchar(
'\n');
}

int main()
{
//freopen("t.txt", "r", stdin);
int t;
scanf(
"%d", &t);
while (t--)
{
n
= 26;
input();
if (!ok())
{
printf(
"***\n");
continue;
}
else
work();
print();
}
return 0;
}
posted @ 2011-07-25 10:30  金海峰  阅读(1280)  评论(0编辑  收藏  举报