这题做的好郁闷啊
题意:很像以前做过的成语接龙,不过比那个难度大点,他是一次输入两个词,然后只要把这些词连起来就行了,起点为sea,终点为sky
这题用到了很多剪枝:
1.把多余的字符串去掉,然后按照升序排序,然后根据你输入的每次两个词,使他们连通起来,构建成一个图,这样每次得到的都是按照字典排序好的
2.预先判断图是否连通,不连通直接输无法找到
3.找出所有不连通的点,这样在进行dfs时,可以节省好多时间。
自己花了点时间写了个代码:
#include<cstdio>
#include<cstring>
char word[20][20];
int n,wcount,m;
int map[20][20];
int vis[20],path[20],tmpPath[20],no[20],vis2[20];
int mDeep,start,end;
int getWord(char *s){
for(int i=0;i<wcount;i++){
if(strcmp(s,word[i])==0){
return i;
}
}
strcpy(word[wcount++],s);
return wcount-1;
}
void dfs(int pos,int deep){
if(deep>wcount){
return;
}
tmpPath[deep]=pos;
if(pos==end){
if(mDeep<deep){
mDeep=deep;
memcpy(path,tmpPath,deep*sizeof(int));
}
if(mDeep==wcount){
return;
}
return;
}
for(int i=0; i<wcount; i++){
if(map[pos][i]&&!vis[i]){
//printf("%d %d\n",pos,i);
vis[i]=1;
dfs(i,deep+1);
if(mDeep==wcount){
return;
}
vis[i]=0;
}
}
}
//剪枝 ,判断点是否可达
int dfs2(int pos,int zd){
if(pos==zd){
return true;
}
for(int i=0;i<wcount;i++){
if(!vis2[i]&&map[pos][i]&&!vis[i]){
vis2[i]=1;
if(dfs2(i,zd)){
return true;
}
}
}
return false;
}
int main(){
int T;
char s1[100][20],s2[100][20];
scanf("%d",&T);
for(int t=0;t<T;t++){
scanf("%d",&n);
//初始化
wcount=0;
memset(map,0,sizeof(map));
memset(vis,0,sizeof(vis));
mDeep=0;
for(int i=0;i<20;i++){
strcpy(word[i],"\0");
}
//储存单词
for(int i=0;i<n;i++){
scanf("%s%s",&s1[i],&s2[i]);
//printf("%s %s\n",s1,s2);
getWord(s1[i]);
getWord(s2[i]);
}
//排序
char c[20];
for(int i=1;i<wcount;i++){
for(int j=0;j<i;j++){
if(strcmp(word[i],word[j])<0){
strcpy(c,word[i]);
strcpy(word[i],word[j]);
strcpy(word[j],c);
}
}
}
//构建图
int a,b;
for(int i=0;i<n;i++){
a=getWord(s1[i]);
b=getWord(s2[i]);
map[a][b]=map[b][a]=1;
//printf("%d %d\n",a,b);
}
//寻找起点和终点
for(int i=0;i<wcount;i++){
if(strcmp(word[i],"sea")==0){
start=i;
}
else if(strcmp(word[i],"sky")==0){
end=i;
}
}
vis[start]=1;
memset(vis2,0,sizeof(vis2));
printf("Case %d: ",t+1);
if(!dfs2(start,end)){
printf("what a pity\n");
continue;
}
//去掉不能到达终点的点
for(int i=0;i<wcount;i++){
memset(vis2,0,sizeof(vis2));
if(!vis[i]){
if(!dfs2(i,end)){
vis[i]=1;
}
}
}
vis[start]=0;
vis[end]=1;
//去掉不能到达起点的点
for(int i=0;i<wcount;i++){
memset(vis2,0,sizeof(vis2));
if(!vis[i]){
if(!dfs2(i,start)){
vis[i]=1;
}
}
}
vis[end]=0;
memset(vis2,0,sizeof(vis2));
vis[start]=1;
dfs(start,0);
for(int i=0;i<mDeep;i++){
if(i==0){
printf("%s",word[path[i]]);
}
else{
printf(" %s",word[path[i]]);
}
}
printf(" sky");
printf("\n");
}
return 0;
}
可是发现老是tle,这都快郁闷死了,明明可以过的,时间复杂度和下面的代码是一样的,可是换成下面的代码31ms就过了,真郁闷啊
#include <cstdio>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <cstdlib>
using namespace std;
char word[20][20],s1[105][20],s2[105][20];
int ws,map[20][20],vis[20],vis2[20],res[20],bres[20],yes,sky,sea,rl;
void init(){
memset(map,0,sizeof map);
for(int i=1;i<=20;i++)strcpy(word[i],"\0");
ws=0;//点数
memset(res,0,sizeof res);
yes=0;
rl=0;
}
//搜连通
int dfslt(int p){
if(p==sky)return 1;
for(int i=1;i<=ws;i++){
if(!vis[i]&&map[p][i]){
vis[i]=1;
if(dfslt(i))return 1;
}
}
return 0;
}
//搜不可到达的点
int dfs2(int p,int dir){
if(p==dir)return 1;
for(int i=1;i<=ws;i++){
if(!vis2[i]&&!vis[i]&&map[p][i]){
vis2[i]=1;
if(dfs2(i,dir))return 1;
}
}
return 0;
}
int dfs(int step,int p){
if(step>ws)return 0;
if(res[step]==sky&&step>rl){
yes=1;
rl=step;
for(int i=0;i<=rl;i++){
bres[i]=res[i];
}
if(rl==ws)return 1;
return 0;
}
for(int i=1;i<=ws;i++){
if(!vis[i]&&map[p][i]==1){
vis[i]=1;
res[step+1]=i;
if(dfs(step+1,i))return 1;
vis[i]=0;
}
}
return 0;
}
int gwords(char *ss){
for(int i=1;i<=ws;i++){
if(strcmp(word[i],ss)==0){
return i;
}
}
ws++;
strcpy(word[ws],ss);
return ws;
}
int main(){
int cas;
scanf("%d",&cas);
for(int ca=1;ca<=cas;ca++){
init();
int n,a,b;
scanf("%d",&n);
//输入字符串 储存
for(int i=1;i<=n;i++){
scanf("%s%s",s1[i],s2[i]);
gwords(s1[i]),gwords(s2[i]);
}
//冒泡排序
char t[20];
for(int i=1;i<=ws-1;i++){
int ind=i;
for(int j=i+1;j<=ws;j++){
if(strcmp(word[j],word[ind])<0)ind=j;
}
if(ind!=i){
strcpy(t,word[ind]);
strcpy(word[ind],word[i]);
strcpy(word[i],t);
}
}
//建图 map[i][j]~按照字符串升序
for(int i=1;i<=n;i++){
a=gwords(s1[i]),b=gwords(s2[i]);
map[a][b]=map[b][a]=1;
}
//判单词是否存在
sky=-1,sea=-1;
for(int i=1;i<=ws;i++){
if(strcmp(word[i],"sky")==0)sky=i;
if(strcmp(word[i],"sea")==0)sea=i;
}
//判sea->sky是否可达
memset(vis,0,sizeof vis);
vis[sea]=1;
//如果不存在单词 或者两者不可达
if(sky==-1||sea==-1||!dfslt(sea)){
printf("Case %d: what a pity\n",ca);
continue;
}
memset(vis,0,sizeof vis);
//强力剪枝,去掉不可到达的点
for(int i=1;i<=ws;i++){
memset(vis2,0,sizeof vis2);
vis2[sea]=1;
if(!dfs2(i,sky))vis[i]=1;
}
for(int i=1;i<=ws;i++){
memset(vis2,0,sizeof vis2);
vis2[sky]=1;
if(!dfs2(i,sea))vis[i]=1;
}
//搜索
vis[sea]=1;
res[1]=sea;
dfs(1,sea);
//输出
if(yes){
printf("Case %d: %s",ca,word[bres[1]]);
for(int i=2;i<=rl;i++){
printf(" %s",word[bres[i]]);
}
printf("\n");
}else{
printf("Case %d: what a pity\n",ca);
}
}
return 0;
}