编码规范
这次两个程序都是用c写的,我比较习惯的风格是main函数在最后,其他函数在前面,而zjoe习惯的编程风格是main函数在第一位,然后前面声明一堆变量。他阅读过一些工业级c的代码,据说很多用c写的库都用这样的风格。
我们两个人编写c语言都比较规范,重视代码的重用性。
当某一个功能重复次数打到3次以上的话,那么就比较有必要为之写一个函数。而模块化到底是不是都是好的呢?我觉得这是运行效率和代码可重用性的trade off,追求效率的代码会用很多小技巧来加速程序的运行,而其可重用行可扩展性就变低了。不过在现在这种程序员时间比机器时间还宝贵的时代,显然编写可重用的代码是要有益得多的。
时间记录
预计时间 | 5h | 实际用时 | 7h |
代码规范 | 0.0h | 0.0h | |
具体设计 | 0.5h | 1h | |
具体编码 | 3.5h | 4h | |
代码复审 | 0.0h | 0h | |
代码测试 | 0.5h | 1h | |
测试报告 | 0.5h | 0.5h |
问题分析
new.c
对于如何构造一个wordsmatrix,我们一开始很难想到一种很好的构造方法,要想穷举所有的可能方法并寻找最优显然是不好写的,而且不一定能出结果。
经过我和我的peer的讨论,发现不妨换一种思路来考虑问题,不去漫无目的在状态空间里寻求最优解。而是限制状态空间(n*m),并且每次随机放单词,取一个估价最大的单词放上去,并且不进行回溯。
这样带来的一个问题是:不能保证每次都能得到解。不过只要把n和m设置得大一点,就能每次都得到解。
我们最后取了估价函数为:放置当前单词、当前位置、当前方向以后所能和以往单词相交的交点个数来作为当前动作的股价。显然我们希望单词之间交叉越多越好。
每次随机M次(程序里取了100000),每次找估价函数值最大的位置,方向。然后把单词放上去。
测试结果发现,例子sample.txt能在限制20*20的情况下构造出解来,而19*19就不行了。这个时候我们决定对单词长度进行排序,意外地发现程序在18*18的情况下构造出解来了。
最后经过分析发现,先放单词长的会更多地体现出估价函数的作用,而100000次随机使得这种股价效果异常明显。
最后对于sample.txt,我们得到上叙18*18的解。
至于如何填满,很容易,,直接填“X”估计就行,实在不行多随机几次就能把空填上(使用我写的test.c进行判断)
具体实现见代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <string.h> 3 #include <time.h> 4 #define MAXNUM 100 5 #define MAXLEN 26 6 #define MAXMAT 100 7 #define MAXRANDOM 100000 8 int dx[8]={-1,-1,0,1,1,1,0,-1}; 9 int dy[8]={0,1,1,1,0,-1,-1,-1}; 10 int board[MAXMAT][MAXMAT]; 11 char words[MAXNUM][MAXLEN]; 12 int N,M; 13 FILE *fin,*fout; 14 void work(int n); 15 int randpos(char *word, int *posx, int *posy, int *posd); 16 int howgood(char *word, int x, int y, int drct); 17 void placeword(char *word, int x, int y, int drct); 18 void swap(char a[],char b[]); 19 int main(int argc,char *argv[]) 20 { 21 int n,i,j; 22 fin=fopen("words.txt","r"); 23 fout=fopen("result.txt","w"); 24 fscanf(fin,"%d",&n); 25 fprintf(fout,"%d\n",n); 26 for(i=1;i<=n;i++) 27 { 28 fscanf(fin,"%s",words[i]); 29 fprintf(fout,"%s\n",words[i]); 30 } 31 for(i=1;i<=n;i++) 32 for(j=i+1;j<=n;j++) 33 if(strlen(words[i])<strlen(words[j])) 34 swap(words[i],words[j]); 35 N=atoi(argv[1]); 36 M=atoi(argv[2]); 37 work(n); 38 39 for(i=1;i<=N;i++) 40 { 41 for(j=1;j<=M;j++) 42 { 43 printf("%c", board[i][j]); 44 fprintf(fout,"%c",board[i][j]); 45 } 46 printf("\n"); 47 fprintf(fout,"\n"); 48 } 49 return 0; 50 } 51 void swap(char a[],char b[]) 52 { 53 int i; 54 char c; 55 for(i = 0;i<MAXLEN;i++) 56 { 57 c=a[i]; 58 a[i]=b[i]; 59 b[i]=c; 60 } 61 } 62 void work(int n) 63 { 64 int i,j,posx,posy,posd,can; 65 for(i=1;i<=N;i++)for(j=1;j<=M;j++)board[i][j]=' '; 66 srand((int)time(0)); 67 for(i=1;i<=n;i++) 68 { 69 can=randpos(words[i],&posx,&posy,&posd); 70 if(can==-1) 71 { 72 printf("cannot!\n"); 73 // getchar(); 74 } 75 placeword(words[i],posx,posy,posd); 76 } 77 } 78 int randpos(char *word, int *posx, int *posy, int *posd) 79 { 80 int max,i,x,y,d,temp; 81 max=-1; 82 for(i=1;i<=MAXRANDOM;i++) 83 { 84 x=rand()%N+1; 85 y=rand()%M+1; 86 d=rand()%8; 87 temp=howgood(word,x,y,d); 88 if(temp>max) 89 { 90 max=temp; 91 *posx=x; 92 *posy=y; 93 *posd=d; 94 } 95 } 96 return max; 97 } 98 int howgood(char *word, int x, int y, int drct) 99 { 100 int xx,yy,score,i; 101 xx=x; 102 yy=y; 103 score=0; 104 for(i=0;i<strlen(word);i++) 105 { 106 if((xx<1 || xx>N || yy<1 || yy>M) || (board[xx][yy]!=' ' && board[xx][yy]!=word[i])) 107 return -1; 108 if(board[xx][yy]==word[i]) 109 score++; 110 xx+=dx[drct]; 111 yy+=dy[drct]; 112 } 113 return score; 114 } 115 void placeword(char *word, int x, int y, int drct) 116 { 117 int xx,yy,i; 118 xx=x; 119 yy=y; 120 for(i=0;i<strlen(word);i++) 121 { 122 board[xx][yy]=word[i]; 123 xx+=dx[drct]; 124 yy+=dy[drct]; 125 } 126 }
test.c
如何验证一个matrix是否满足条件1-3是一个很简单的p问题,最简单最暴力的是枚举8*n*m*(n+m)/2的子串,然后再和单词表进行比对,最后再判断一下是否四个角都填上了,是否有空行列。就能解决问题。
为了保证效率,我使用了Trie(单词查找树)进行匹配串。Trie是一种常用的数据结构,能够很好地实现字典查找。本质上也是一种Hash函数。
基本结构如下:
这么以来,查找的复杂度就变成了O(n*m),是一种比较高效的方法。
具体实现见代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #define MAXTREESIZE 2000 4 #define MAXLEN 22 5 #define MAXNUM 63 6 #define MAXMAT 100 7 8 struct Node 9 { 10 int flag; 11 int p[26]; 12 }; 13 14 struct Go 15 { 16 int x; 17 int y; 18 }; 19 20 struct Node queue[MAXTREESIZE]; 21 struct Go pos[8] = { 22 {0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1} 23 }; 24 /* 25 * note for *pos* 26 * 0 is right 27 * 1 is right down 28 * 2 is down 29 * 3 is ... you know 30 * it is deasil(顺时针) 31 * 32 */ 33 char words[MAXNUM][MAXLEN],matrix[MAXMAT][MAXMAT],out[MAXMAT*2][MAXMAT*2]; 34 int numw,n,m,numnode,boo[MAXNUM]; 35 36 int ctoint(char c) 37 { 38 if (c >= 'a' && c<='z') return c-'a'; 39 if (c >= 'A' && c<='Z') return c-'A'; 40 return -1; 41 } 42 43 int newnode() 44 { 45 int i; 46 queue[numnode].flag = -1; 47 for(i = 0;i<26;i++) 48 queue[numnode].p[i] = -1; 49 numnode += 1; 50 return numnode-1; 51 } 52 53 int insertnode(int flag,char s[]) 54 { 55 int i,id = 0,cid; 56 for(i = 0;i<strlen(s)-1;i++) 57 { 58 if (ctoint(s[i]) == -1) 59 return -1; 60 cid = queue[id].p[ctoint(s[i])]; 61 if (cid == -1) 62 { 63 cid = newnode(); 64 queue[id].p[ctoint(s[i])] = cid; 65 } 66 id = cid; 67 } 68 queue[id].flag = flag; 69 return id; 70 } 71 72 int searchnode(int x,int y, int p) 73 { 74 int id = 0,cid; 75 for(;x>=0&&y>=0&&x<n&&y<m; x+=pos[p].x,y+=pos[p].y) 76 { 77 cid = queue[id].p[ctoint(matrix[x][y])]; 78 if (cid == -1) 79 return -1; 80 id = cid; 81 if (queue[id].flag != -1) 82 return queue[id].flag; 83 } 84 return -1; 85 } 86 87 int testallword() 88 { 89 int i,j,p,k; 90 for(i=0;i<n;i++) 91 for(j=0;j<m;j++) 92 for(p=0;p<8;p++) 93 { 94 k = searchnode(i,j,p); 95 if (k != -1) 96 { 97 //printf("%d %d %c %d %s",i,j,matrix[i][j],p,words[k]); 98 boo[k] += 1; 99 } 100 } 101 for(i=0;i<numw;i++) 102 { 103 //printf("%d\n",boo[i]); 104 if (boo[i]==0) 105 return -1; 106 } 107 return 0; 108 } 109 110 void addtag(int x,int y,int p,int k) 111 { 112 int i,g; 113 char c; 114 for(i = 0;i < strlen(words[k])-2;i++) 115 { 116 if (p == 0 || p == 4) c = '-'; 117 if (p == 1 || p == 5) c = '\\'; 118 if (p == 2 || p == 6) c = '|'; 119 if (p == 3 || p == 7) c = '/'; 120 out[2*x + pos[p].x][2*y + pos[p].y] = c; 121 x += pos[p].x; 122 y += pos[p].y; 123 } 124 return ; 125 } 126 127 void outans() 128 { 129 int i,j,p,k; 130 for(i=0;i<n;i++) 131 for(j=0;j<m;j++) 132 { 133 out[i*2][j*2] = matrix[i][j]; 134 out[i*2+1][j*2] = ' ' ; 135 out[i*2][j*2+1] = ' ' ; 136 out[i*2+1][j*2+1] = ' ' ; 137 } 138 for(i=0;i<n;i++) 139 for(j=0;j<m;j++) 140 for(p=0;p<8;p++) 141 { 142 k = searchnode(i,j,p); 143 if(k!=-1) 144 addtag(i,j,p,k); 145 } 146 for(i=0;i<n*2-1;i++) 147 printf("\t%s\n",out[i]); 148 } 149 150 int testcorner() 151 { 152 if(out[0][1]==' ' && out[1][0]==' ' && out[1][1]==' ') 153 return -1; 154 if(out[2*n-1][0]==' ' && out[2*n][1]==' ' && out[2*n-1][1]==' ') 155 return -1; 156 if(out[0][2*m-1]==' ' && out[1][2*m]==' ' && out[1][2*m-1]==' ') 157 return -1; 158 if(out[2*n-1][2*m]==' ' && out[2*n][2*m-1]==' ' && out[2*n-1][2*m-1]==' ') 159 return -1; 160 return 0; 161 } 162 163 int main(int argc,char *argv[]) 164 { 165 int i,j; 166 if (argc == 1) 167 { 168 freopen("sample.txt","r",stdin); 169 } 170 if (argc >=2) 171 { 172 if(freopen(argv[1],"r",stdin)==NULL) 173 { 174 printf("please input the corrent <file name>\n"); 175 return -1; 176 } 177 } 178 if (argc >=3) 179 { 180 freopen(argv[2],"w",stdout); 181 } 182 183 scanf("%d\n",&numw); 184 newnode(); 185 for(i = 0; i<numw;i++) 186 { 187 fgets(words[i],MAXLEN,stdin); 188 if ((j = insertnode(i,words[i]))==-1) 189 return -1; 190 } 191 while(fgets(matrix[n],MAXMAT,stdin)>0) 192 n += 1; 193 m = strlen(matrix[0])-1; 194 195 if(testallword()==0) 196 { 197 printf("STATE1:\tis good!\n"); 198 outans(); 199 } 200 else 201 { 202 printf("STATE1:\tbad!\n"); 203 printf("\tYou leave the words belove(no used or used more than once):\n"); 204 for(i=0;i<numw;i++) 205 if(boo[i]!=1) 206 printf("\tused times = %d , %s",boo[i],words[i]); 207 } 208 if(n==m) 209 printf("\nSTATE2:\tis good! n&m = %d\n",n); 210 else 211 printf("\nSTATE2:\tbad! n=%d m=%d\n",n,m); 212 if(testcorner() == 0) 213 printf("\nSTATE3:\tis good!\n"); 214 else 215 printf("\nSTATE3:\tbad!\n"); 216 return 0; 217 }
WordsSearch生成,验证工具说明
new.c :读入words.txt 生成 result.txt
test.c :读入(默认读入sample.txt 需要指定参数伟result.txt) 屏幕生成测试结果说明
words.txt :第一行一个整数n,表示以下有n行单词
result.txt :new生成的结果,第一行一个整数n,以下n行单词,最后输出一个矩阵
sample.txt :老师网站上的例子
new :new.c的可执行文件(under linux)
new.c使用方法
gcc new.c -o new
./new 18 18 #生成18*18的结果矩阵(有很小的几率出错)
目前可以生成的最优矩阵为18*18
K MXUNIL OFAQ
SPEYELIMSURIVPOU
AI DOCUMENT ENS
VHDROWSSAPPRINTER
TECCAPMENUFREEWARE
BRSCOOINOCITOME NR
SOUBLIMULTIMEDIAA
CPUPYFCYBERSPACEMW
Y NLEASBUGO SDRWEY
DOKRAQTBJ O EPLP
TCAIEICPEAWIBVOIS
OADTTUPNINNB RTFW
OR EAU HLHRD EKPO
JBDN HBLOEOAEWSSID
PEHSARCAOWRVCHIEZN
EREVIRDASSSA KTD I
GIGABYTECEEJL EETW
FIREWIRE ERAWDRAH
test result from my test.c
STATE1: is good!
K M X-U-N-I-L O F-A-Q
\ \ / | |
S P E Y-E-L-I-M-S-U-R-I-V P O U
|\| \ / | | |
A I D-O-C-U-M-E-N-T E N S
| |\ |/ | | |
V H D-R-O-W-S-S-A-P P-R-I-N-T-E-R
| | /| \ |
T E C C A P M-E-N-U F-R-E-E-W-A-R-E
\ / | | / | |
B R S C O O I N-O-C-I-T-O-M-E N R
\ \|/ | | | / | |
S O U B L I M-U-L-T-I-M-E-D-I-A A
\|\| | | | /| | |
C-P-U P Y F C-Y-B-E-R-S-P-A-C-E M W
/ | |\| | | / | | |
Y N L E A S B-U-G O S D R W E Y
|\| |\| | / | |/ |/ | |
D O K R A Q T B J O E P L P
|\|\ |\ / \ \| /| /| | | |
T C A I E I-C-P E A W I B V O I S
| | |\ \|/ \ \|\ |/ | | | |
O A D T T-U-P-N-I N N B R T F W
| | / \ \ /|\ \ | | | |
O R E A U H L H R D E K P O
| | / \ \ \ / |\ \ \ | | | |
J B D N H B L O E O A E W S S I D
| | \ \ \ /| |\ \ \ | | |
P E H-S-A-R-C A O W R V C H I E Z N
| | \ \ \ |\| \ \ \| |
E R-E-V-I-R-D A S S S A K T D I
| \ \ \| |\ \ \ \ |
G-I-G-A-B-Y-T-E C E E J L E E T W
/ \ \ \
F-I-R-E-W-I-R-E E-R-A-W-D-R-A-H
STATE2: is good! n&m = 18
STATE3: is good!
test.c使用方法
输出结果显示是否满足1,2,3条件
1、每个单词只出现且必须出现一次、没空行空列
2、长宽必须一样
3、四个角必须被单词包含