NYOJ-99 单词拼接(欧拉+回溯)

单词拼接

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 5
 
描述

给你一些单词,请你判断能否把它们首尾串起来串成一串。

前一个单词的结尾应该与下一个单词的道字母相同。

 

aloha

dog

arachnid

gopher

tiger

rat

 

可以拼接成:aloha.arachnid.dog.gopher.rat.tiger

 

 
输入
第一行是一个整数N(0<N<20),表示测试数据的组数
每组测试数据的第一行是一个整数M,表示该组测试数据中有M(2<M<1000)个互不相同的单词,随后的M行,每行是一个长度不超过30的单词,单词全部由小写字母组成。
输出
如果存在拼接方案,请输出所有拼接方案中字典序最小的方案。(两个单词之间输出一个英文句号".")
如果不存在拼接方案,则输出
***
样例输入
2

6

aloha

arachnid

dog

gopher

rat

tiger

3

oak

maple

elm
样例输出
aloha.arachnid.dog.gopher.rat.tiger

***
来源
Waterloo local 2003.01.25 /POJ

解析:

有向图G 为欧拉回路:
当且仅当G 的基图连通,且所有顶点的入度等于出度。
有向图G 为欧拉路:
当且仅当G 的基图连通,且只存在一个顶点u 的入度比出度大1、
只存在一个顶点v 的入度比出度小1,其它所有顶点的入度等于出度。本题还有要注意的一点就是:

单词的首字母应该看做是出度,尾字母看做是入度

代码一:----TLE
我先用并查集判断是否连通,然后再据欧拉路的条件判断是否能形成欧拉路,
最后才搜索字典序最小,超时也不足为奇,不过为此我也纠结了好几天,后来想了想可以不用使用并查集判断连通,可以再最后的搜索中边搜索边判断是否连通

 

NYOJ-99 单词拼接(欧拉+回溯) View Code
  1 #include <cstdio>

  2 #include <cstring>

  3 #include <cmath>

  4 #include <iostream>

  5 #include <algorithm>

  6 

  7 using namespace std;

  8 

  9 const int M = 1005;

 10 struct word

 11 {

 12     char s[35];

 13     int start;

 14     int end;

 15 }str[M];

 16 int in_degree[26], out_degree[26]; // 分别记录入度和出度

 17 int father[26];   // 并查集中的数组

 18 bool use[26];   //记录首尾出现的字母,用并查集时即判断出现过的字母是否连通

 19 bool visit[M];  // 搜索路径时候回溯时候用的

 20 int ans[M];    // 结果序列

 21 int n, flagOfStart;

 22 /*要判断存在欧拉路径后输出字典序最小的路径,

 23 先把单词按字典序排序,然后判断是欧拉回路还是只是欧拉通路,

 24 如果是欧拉回路,直接从第0号单词开始搜,否则,找到出度比入度大1的

 25 那个单词开始搜标记为欧拉路起点的字母,flagOfStart即记录起点编号;

 26 

 27 

 28 int find_father(int i)

 29 {

 30     if(father[i] == -1)

 31         return i;

 32     else

 33         return find_father(father[i]);

 34 }

 35 

 36 void merge(int num1, int num2)

 37 {

 38     num1 = find_father(num1);

 39     num2 = find_father(num2);

 40     if(num1 != num2)

 41         father[num1] = num2;

 42 }

 43 

 44 void init()

 45 {

 46     memset(in_degree, 0, sizeof(in_degree));

 47     memset(out_degree, 0, sizeof(out_degree));

 48     memset(father, -1, sizeof(father));

 49     memset(visit, false, sizeof(visit));

 50     memset(use, false, sizeof(use));

 51     scanf("%d", &n);

 52     for(int i = 0; i < n; ++i)

 53     {

 54         scanf("%s", str[i].s);

 55         int len = strlen(str[i].s);

 56         str[i].start = str[i].s[0] - 'a';

 57         str[i].end = str[i].s[len-1] - 'a';

 58         use[str[i].start] = use[str[i].end] = true;

 59         merge(str[i].start, str[i].end);

 60         ++out_degree[str[i].start];

 61         ++in_degree[str[i].end];

 62     }

 63 }

 64 

 65 bool judge()

 66 {

 67     flagOfStart = 0;

 68     int cnt_in = 0;

 69     int cnt_out = 0;

 70     int tmp = 0;

 71     for(int i = 0; i < 26; ++i) //并查集判断是否连通

 72     {

 73         if(use[i] && father[i] == -1)

 74         {

 75             ++tmp;

 76             if(tmp > 1)    // 不连通

 77                 return false;

 78         }

 79     }

 80     for(int i = 0; i < 26; ++i)  // 判断是否能形成欧拉路或欧拉回路

 81     {

 82         if(!use[i])

 83             continue;

 84         if(abs(in_degree[i] - out_degree[i]) >= 2)

 85             return false;

 86 

 87         // 下边这点我把起点弄错了,害我调试了半天(我把出度和入度理解反了)

 88         if(in_degree[i] - out_degree[i] == 1) //入度比出度大一,若为欧拉路则为唯一的终点

 89             ++cnt_in;

 90         else if(in_degree[i] - out_degree[i] == -1)//入度比出度小一,若为欧拉路则为唯一的起点

 91         {

 92             ++cnt_out;

 93             flagOfStart = i;   // 若是欧拉路,则i必然是起点

 94         }

 95     }

 96     if(!flagOfStart)   //  如果是欧拉回路,则找到任意第一个出现过的字母作为搜索的起点即可

 97     {

 98         for(int i = 0; i < 26; ++i)

 99             if(use[i])

100             {

101                 flagOfStart = i;

102                 break;

103             }

104     }

105     if(cnt_out == 0 && cnt_in ==0 || cnt_out == 1 && cnt_in == 1)

106         return true;

107     return false;

108 }

109 

110 bool cmp(word p, word q)

111 {

112     return strcmp(p.s, q.s) < 0;

113 }

114 

115 void DFS(int cur)  // 在进行DFS的时候已经确定了本题的答案一定存在,

116 {                   //只是找到字典序最小的那组序列即可

117     if(cur == n)

118     {

119         for(int i = 0; i < n-1; ++i)

120             printf("%s.", str[ans[i]].s);

121         printf("%s\n", str[ans[n-1]].s);

122         return;

123     }

124     for(int i = 0; i < n; ++i)

125     {

126         if(str[i].start < flagOfStart || visit[i])

127             continue;

128         if(str[i].start > flagOfStart)

129             return;

130         ans[cur] = i;

131         visit[i] = true;

132         flagOfStart = str[i].end;

133         DFS(cur+1);

134         visit[i] = false;

135     }

136 }

137 

138 int main()

139 {

140     int T;

141     scanf("%d", &T);

142     while(T--)

143     {

144         init();

145         if(!judge())

146         {

147             printf("***\n");

148             continue;

149         }

150         sort(str, str + n, cmp);

151         DFS(0);

152     }

153     return 0;

154 }

 

代码二: -----没用并查集,还是超时fuck。。。。。

NYOJ-99 单词拼接(欧拉+回溯) View Code
  1 #include <cstdio>

  2 #include <cstring>

  3 #include <cmath>

  4 #include <iostream>

  5 #include <algorithm>

  6 

  7 using namespace std;

  8 

  9 const int M = 1005;

 10 struct word

 11 {

 12     char s[35];

 13     int start;

 14     int end;

 15 }str[M];

 16 int in_degree[26], out_degree[26];

 17 bool use[26];   

 18 bool visit[M];

 19 int ans[M];

 20 int n, flagOfStart;

 21 

 22 void init()

 23 {

 24     memset(in_degree, 0, sizeof(in_degree));

 25     memset(out_degree, 0, sizeof(out_degree));

 26     memset(visit, false, sizeof(visit));

 27     memset(use, false, sizeof(use));

 28     scanf("%d", &n);

 29     for(int i = 0; i < n; ++i)

 30     {

 31         scanf("%s", str[i].s);

 32         int len = strlen(str[i].s);

 33         str[i].start = str[i].s[0] - 'a';

 34         str[i].end = str[i].s[len-1] - 'a';

 35         use[str[i].start] = use[str[i].end] = true;

 36         ++out_degree[str[i].start];

 37         ++in_degree[str[i].end];

 38     }

 39 }

 40 

 41 bool judge()

 42 {

 43     flagOfStart = 0;

 44     int cnt_in = 0;

 45     int cnt_out = 0;

 46     int tmp = 0;

 47     for(int i = 0; i < 26; ++i)

 48     {

 49         if(!use[i])

 50             continue;

 51         if(abs(in_degree[i] - out_degree[i]) >= 2)

 52             return false;

 53         if(in_degree[i] - out_degree[i] == 1) //入度比出度大一,若为欧拉路则为唯一的终点

 54             ++cnt_in;

 55         else if(in_degree[i] - out_degree[i] == -1)//入度比出度小一,若为欧拉路则为唯一的起点

 56         {

 57             ++cnt_out;

 58             flagOfStart = i;   // 若是欧拉路,则i必然是起点

 59         }

 60     }

 61     if(!flagOfStart)   //  如果是欧拉回路,则找到任意第一个出现过的字母作为搜索的起点即可

 62     {

 63         for(int i = 0; i < 26; ++i)

 64             if(use[i])

 65             {

 66                 flagOfStart = i;

 67                 break;

 68             }

 69     }

 70     if(cnt_out == 0 && cnt_in ==0 || cnt_out == 1 && cnt_in == 1)

 71         return true;

 72     return false;

 73 }

 74 

 75 bool cmp(word p, word q)

 76 {

 77     return strcmp(p.s, q.s) < 0;

 78 }

 79 

 80 bool DFS(int cur)

 81 {                   

 82     if(cur == n)

 83     {

 84         return true;

 85     }

 86     for(int i = 0; i < n; ++i)

 87     {

 88         if(str[i].start < flagOfStart || visit[i])

 89             continue;

 90         if(str[i].start > flagOfStart)

 91             return false;

 92         ans[cur] = i;

 93         visit[i] = true;

 94         flagOfStart = str[i].end;

 95         if(DFS(cur+1))

 96             return true;

 97         visit[i] = false;

 98     }

 99     return false;

100 }

101 

102 int main()

103 {

104     int T;

105     scanf("%d", &T);

106     while(T--)

107     {

108         init();

109         if(!judge())

110         {

111             

112         }

113         sort(str, str + n, cmp);

114         if(!DFS(0))

115         {

116            printf("***\n");

117             continue; 

118         }

119         for(int i = 0; i < n-1; ++i)

120             printf("%s.", str[ans[i]].s);

121         printf("%s\n", str[ans[n-1]].s);

122     }

123     return 0;

124 }

代码三:----AC,哥桑心了

  1 #include<stdio.h>

  2 #include<string.h>

  3 #include<algorithm>

  4 using namespace std;

  5 struct node

  6 {

  7     char s[31];

  8     int first,last;

  9 };

 10 

 11 node a[1001];

 12 int degree_in[1001],degree_out[1001],m,order[1001];

 13 bool used[1001];

 14 

 15 int f()

 16 {

 17     int x1,x2,ans=0,i;

 18     x1=x2=0;

 19     for(i=0;i<26;++i)

 20     {

 21         if(abs(degree_in[i]-degree_out[i])>=2)

 22             return -1;

 23         else if(degree_in[i]-degree_out[i]==1)

 24             x1++;

 25         else if(degree_in[i]-degree_out[i]==-1)

 26         {

 27             x2++;

 28             ans=i;

 29         }

 30     }

 31         if(x1>1||x2>1) //当时三个度时,必定是 12 和21,相同的不能大于等于2,不然不能构成欧拉回路

 32             return -1;

 33         else if(x1==0)

 34         {

 35             for(i=0;i<26;++i)

 36                 if(degree_out[i])

 37                     return i; //找到一个就行

 38         }

 39         else

 40             return ans;

 41 

 42 }

 43 bool cmp(node a,node b)

 44 {

 45     return strcmp(a.s,b.s)<0;

 46 }

 47 

 48 bool dfs(int st,int cnt)

 49 {

 50     int i;

 51     if(cnt==m)

 52         return 1;

 53     for(i=0;i<m;++i)

 54     {

 55         if(a[i].first<st||used[i])

 56             continue;

 57         else if(a[i].first>st)

 58             return false;

 59         used[i]=true;

 60         order[cnt]=i;

 61         if(dfs(a[i].last,cnt+1))

 62             return 1;

 63         used[i]=false;//回溯判断是否形成欧拉路径

 64     }

 65     return false;

 66 }

 67 

 68 

 69 int main()

 70 {

 71     int N,len,i,start;

 72     scanf("%d",&N);

 73     while(N--)

 74     {

 75         memset(used,false,sizeof(used));

 76         memset(degree_out,0,sizeof(degree_out));

 77         memset(degree_in,0,sizeof(degree_in));

 78         scanf("%d",&m);

 79         for(i=0;i<m;++i)

 80         {

 81             scanf("%s",a[i].s);

 82             len = strlen(a[i].s);

 83             a[i].first =a[i].s[0]-'a';

 84             a[i].last =a[i].s[len-1]-'a';

 85             degree_out[a[i].s[0]-'a']++;

 86             degree_in[a[i].s[len-1]-'a']++;//注意这里的入肚出度

 87         }

 88         start=f();

 89         if(start ==-1)

 90         {

 91             printf("***\n");

 92             continue;

 93         }

 94         sort(a,a+m,cmp);

 95         if(!dfs(start,0))

 96         {

 97             printf("***\n");

 98             continue;

 99         }

100        printf("%s",a[order[0]].s);

101       for(i=1;i<m;i++)

102          printf(".%s",a[order[i]].s);

103       printf("\n");

104     }

105     return 0;

106 }

 

 

你可能感兴趣的:(回溯)