homework-04

Word Search

编码规范

用c写的。因为我们最初定位就是写一个简单粗暴的方法,涉及不到很复杂的类。当然最主要的原因还是we like c!

由于本着“简单”的思想,写函数的时候尽量考虑到了功能的明确与分离,这样代码更易读。

思路

初看这个题感觉相当难。连最朴素的算法都很难写,状态太多了。老师请来的同事讲解之后也没什么收获,都是最容易想到的东西。难写不怕,求出的解差也不怕,就怕又难写结果又差,花很大功夫写出一个很难看的东西,是我最不愿意见到的。跟小伙伴纠结了好久,怎么都感觉无从下手。一直到周一晚上都没写(也有一部分原因是本系课程很紧,实在没什么时间写这个。因为之前说周二中午前交的,所以准备周一晚上和peer交流一下)。

上课的时候老师给大家展示作业,发现还是有人做的很漂亮的,说明这个题还是能做,于是晚上回去又和peer仔细讨论了一下。由于时间不多,我们准备写一个近似算法,尽量用简单的方式考虑问题,并引入一些随机化的方法。经过讨论,得出初步想法:

  1. 写一个程序,给定长宽限制和所有word,看是否能得出可行解。把求最优变为判定性问题。
  2. 先随机放一个word,之后的每个word都要和当前连起来的word相交,如果实在不行,再随机找一个位置。
  3. 用这个word与之前已经填好的部分的相交的字母数量衡量这种放法的好坏。每个单词都取最优的方法,不进行回溯(这是比较关键的地方,回溯的时间复杂度是很难接受的)。
  4. 四角先暂时不考虑。

以上的想法看起来十分简单,并且感觉效果应该很差。无奈作业总是要教的,先写出来再说。在写的过程中,为了简化程序,考虑到没有必要一个新的word一定要和之前的相交,那样的话写起来很麻烦,不如直接找一个最好的位置。这个“最好”可以直接用3中的估价函数评判。如果与之前不相交就是0。可以直接在所有的位置和方向中找到一个最好的位置放入。在这里我们又引入了随机化的方法,对位置和word摆放的方向进行10万次随机。现在算法又变成了这样:

  1. 同上
  2. 对位置和方向随机10万次,找到“最好”的位置,将word放入,如果有一个词找不到位置则认为失败,直接跳出。

这样算法变的极为简单。用57个词的例子实验,最小可以得出20x19的结果。这已经是比较优的结果了。可喜的是程序非常简单,只有70行。这比我们最初想象中的容易了太多。

之后又想到越长的单词限制越多,如果放到最后再考虑,很可能放不进去。于是我们将words按长度从大到小排了序,优先放长单词,这样结果应该会好很多。不出所料,排序后轻松跑出了18x18的矩形。

由于我们写的new程序是一个判定性的程序,根据给定的大小判断是否可行,所以还需要多次运行,手动缩小矩阵,不过这是比较容易的。题目要求四角都有单词,而我们算法中并没有限制这一点,所以也需要多次运行,找到一个符合要求的解。由于结果比较紧凑,所以找到四角都有单词的解还是很容易的。还有一个优点是每次生成的结果都不同,所以可以产生多个不同的word search。

forwil同学又做了一个test程序用来检验结果的正确性,同时给出比较可视化的输出结果。

代码

#include<stdio.h>
#include<string.h>
#define MAXTREESIZE 2000
#define MAXLEN 22
#define MAXNUM 63
#define MAXMAT 100

struct Node
{
    int flag;
    int p[26];
};

struct Go
{
    int x;
    int y;
};

struct Node queue[MAXTREESIZE];
struct Go pos[8] = {
{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}
};
/* 
 * note for *pos*
 * 0 is right
 * 1 is right down
 * 2 is down
 * 3 is ... you know
 * it is deasil(顺时针)
 *
 */
char words[MAXNUM][MAXLEN],matrix[MAXMAT][MAXMAT],out[MAXMAT*2][MAXMAT*2];
int numw,n,m,numnode,boo[MAXNUM];

int ctoint(char c)
{
    if (c >= 'a' && c<='z') return c-'a';
    if (c >= 'A' && c<='Z') return c-'A';
    return -1;
}

int newnode()
{
    int i;
    queue[numnode].flag = -1;
    for(i = 0;i<26;i++)
        queue[numnode].p[i] = -1;
    numnode += 1;
    return numnode-1;
}

int insertnode(int flag,char s[])
{
    int i,id = 0,cid;
    for(i = 0;i<strlen(s)-1;i++)
        {
        if (ctoint(s[i]) == -1)
            return -1;
        cid = queue[id].p[ctoint(s[i])];
        if (cid == -1)
            {
            cid = newnode();
            queue[id].p[ctoint(s[i])] = cid;
            }
        id = cid;
        }
    queue[id].flag = flag;
    return id;
}

int searchnode(int x,int y, int p)
{
    int id = 0,cid;
    for(;x>=0&&y>=0&&x<n&&y<m; x+=pos[p].x,y+=pos[p].y)
        {
        cid = queue[id].p[ctoint(matrix[x][y])];
        if (cid == -1)
            return -1;
        id = cid;
        if (queue[id].flag != -1)
            return queue[id].flag;
        }
    return -1;
}

int testallword()
{
    int i,j,p,k;
    for(i=0;i<n;i++)
        for(j=0;j<m;j++)    
            for(p=0;p<8;p++)
            {
                k = searchnode(i,j,p);
                if (k != -1)
                    {
                    //printf("%d %d %c %d %s",i,j,matrix[i][j],p,words[k]);
                    boo[k] += 1;
                    }
            }
    for(i=0;i<numw;i++)
        {
        //printf("%d\n",boo[i]);
        if (boo[i]==0)
            return -1;
        }
    return 0;
}

void addtag(int x,int y,int p,int k)
{
    int i,g;
    char c;
    for(i = 0;i < strlen(words[k])-2;i++)
        {
        if (p == 0 || p == 4) c = '-';
        if (p == 1 || p == 5) c = '\\';
        if (p == 2 || p == 6) c = '|';
        if (p == 3 || p == 7) c = '/';
        out[2*x + pos[p].x][2*y + pos[p].y] = c;
        x += pos[p].x;
        y += pos[p].y;
        }
    return ;
}

void outans()
{
    int i,j,p,k;
    for(i=0;i<n;i++)
        for(j=0;j<m;j++)
            {
            out[i*2][j*2] = matrix[i][j];
            out[i*2+1][j*2] = ' ' ;
            out[i*2][j*2+1] = ' ' ;
            out[i*2+1][j*2+1] = ' ' ;
            }
    for(i=0;i<n;i++)
        for(j=0;j<m;j++)
            for(p=0;p<8;p++)
            {
                k = searchnode(i,j,p);
                if(k!=-1)
                    addtag(i,j,p,k);
            }
    for(i=0;i<n*2-1;i++)
        printf("\t%s\n",out[i]);
}

int testcorner()
{
    if(out[0][1]==' ' && out[1][0]==' ' && out[1][1]==' ')
        return -1;
    if(out[2*n-1][0]==' ' && out[2*n][1]==' ' && out[2*n-1][1]==' ')
        return -1;
    if(out[0][2*m-1]==' ' && out[1][2*m]==' ' && out[1][2*m-1]==' ')
        return -1;
    if(out[2*n-1][2*m]==' ' && out[2*n][2*m-1]==' ' && out[2*n-1][2*m-1]==' ')
        return -1;
    return 0;
}

int main(int argc,char *argv[])
{
    int i,j;
    if (argc == 1)
    {
        freopen("sample.txt","r",stdin);
    }
    if (argc >=2)
    {
        if(freopen(argv[1],"r",stdin)==NULL)
            {
            printf("please input the corrent <file name>\n");
            return -1;
            }
    }
    if (argc >=3)
    {
        freopen(argv[2],"w",stdout);
    }

    scanf("%d\n",&numw);
    newnode();
    for(i = 0; i<numw;i++)
        {
        fgets(words[i],MAXLEN,stdin);
        if ((j = insertnode(i,words[i]))==-1)
            return -1;
        }
    while(fgets(matrix[n],MAXMAT,stdin)>0)
        n += 1; 
    m = strlen(matrix[0])-1;

    if(testallword()==0)
        {
        printf("STATE1:\tis good!\n");
        outans();
        }
    else
        {
        printf("STATE1:\tbad!\n");
        printf("\tYou leave the words belove(no used or used more than once):\n");
        for(i=0;i<numw;i++)
            if(boo[i]!=1)
                printf("\tused times = %d , %s",boo[i],words[i]);
        }
    if(n==m)
        printf("\nSTATE2:\tis good! n&m = %d\n",n);
    else 
        printf("\nSTATE2:\tbad! n=%d m=%d\n",n,m);
    if(testcorner() == 0)
        printf("\nSTATE3:\tis good!\n");
    else
        printf("\nSTATE3:\tbad!\n");
    return 0;
}
test.c
#include <stdio.h>
#include <string.h>
#include <time.h>
#define MAXNUM 100
#define MAXLEN 26
#define MAXMAT 100
#define MAXRANDOM 100000
int dx[8]={-1,-1,0,1,1,1,0,-1};
int dy[8]={0,1,1,1,0,-1,-1,-1};
int board[MAXMAT][MAXMAT];
char words[MAXNUM][MAXLEN];
int N,M;
FILE *fin,*fout;
void work(int n);
int randpos(char *word, int *posx, int *posy, int *posd);
int howgood(char *word, int x, int y, int drct);
void placeword(char *word, int x, int y, int drct);
void swap(char a[],char b[]);
int main(int argc,char *argv[])
{
    int n,i,j;
    fin=fopen("words.txt","r");
    fout=fopen("result.txt","w");
    fscanf(fin,"%d",&n);
    fprintf(fout,"%d\n",n);
    for(i=1;i<=n;i++)
    {
        fscanf(fin,"%s",words[i]);
        fprintf(fout,"%s\n",words[i]);
    }
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
        if(strlen(words[i])<strlen(words[j]))    
            swap(words[i],words[j]);
    N=atoi(argv[1]);
    M=atoi(argv[2]);
    work(n);
    
    for(i=1;i<=N;i++)
    {
        for(j=1;j<=M;j++)
            {
            printf("%c", board[i][j]);
            fprintf(fout,"%c",board[i][j]);
            }
        printf("\n");
        fprintf(fout,"\n");
    }
    return 0;
}
void swap(char a[],char b[])
{
    int i; 
    char c;
    for(i = 0;i<MAXLEN;i++)
        {
        c=a[i];
        a[i]=b[i];
        b[i]=c;
        }
}
void work(int n)
{
    int i,j,posx,posy,posd,can;
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)board[i][j]=' ';
    srand((int)time(0));
    for(i=1;i<=n;i++)
    {
        can=randpos(words[i],&posx,&posy,&posd);
        if(can==-1)
        {
            printf("cannot!\n");
        //    getchar();
        }
        placeword(words[i],posx,posy,posd);
    }
}
int randpos(char *word, int *posx, int *posy, int *posd)
{
    int max,i,x,y,d,temp;
    max=-1;
    for(i=1;i<=MAXRANDOM;i++)
    {
        x=rand()%N+1;
        y=rand()%M+1;
        d=rand()%8;
        temp=howgood(word,x,y,d);
        if(temp>max)
        {
            max=temp;
            *posx=x;
            *posy=y;
            *posd=d;
        }
    }
    return max;
}
int howgood(char *word, int x, int y, int drct)
{
    int xx,yy,score,i;
    xx=x;
    yy=y;
    score=0;
    for(i=0;i<strlen(word);i++)
    {
        if((xx<1 || xx>N || yy<1 || yy>M) || (board[xx][yy]!=' ' && board[xx][yy]!=word[i]))
            return -1;
        if(board[xx][yy]==word[i])
            score++;
        xx+=dx[drct];
        yy+=dy[drct];
    }
    return score;    
}
void placeword(char *word, int x, int y, int drct)
{
    int xx,yy,i;
    xx=x;
    yy=y;
    for(i=0;i<strlen(word);i++)
    {
        board[xx][yy]=word[i];
        xx+=dx[drct];
        yy+=dy[drct];
    }
} 
new.c

 

结果

new与test的使用方法在github的readme中

18x18

L   Y I-N-P-U-T-R-O-J-A-N-H-O-R-S-E 
|   |                /              
I   E-T-H-E-R-N-E-T-I-Q-U-E-T-T-E B 
|   |              /           / /  
N   L B-A-N-D-W-I-D-T-H-S-A-R-C U   
|   |   |      \ /           / /    
U   I M S-E-R-V-E-R E-T-Y-B-A-G-I-G 
|   | | |      / \         /        
X   M E C D   M   B T-R-O-P E S     
    | | | |  /     \     / /  |     
P   S D I A I D U D B   S V   C-P-U 
|     | | |/   \ \ / \ / /    | |   
R V N-O-I-T-U-L-O-S-E-R-A-W-D-R-A-H 
| |   |  /|      / \ / /      | |   
I I   M L A S   U C E S-O-I-B O S   
| |    /| | |      / \ \ \    | |   
N R   U A B W   G B U R K W   L S   
| |  /  | | |    \   \ \ \ \  | |   
T U M D-R-A-O-B-Y-E-K M N T S L W   
| |     | | |  /   \   \ \ \ \  |   
E S N M E S D C D   P   E A O E O P 
| |  \| | | |   |    \   \ \ \ \| | 
R P   E H E N   A-V-A-J R N M P R I 
| |   |\|   |   |        \ \ \  | | 
A Y R N P-C-I P-O-L-F-A-R-E-T E D H 
| | | | |\  |   |          \      | 
W W E U I O W E-L-I-F-P-I-Z K     C 
| | |   |       |            \    | 
E A V D-R-A-C-D-N-U-O-S F C   C Q A 
| | |   |    /  |        \ \   \| | 
E-R-I-W-E-R-I-F W   R-E-B-O-O-T A C 
| | |   |  /    |          \ \  |\| 
R E R   P S E-M-O-T-I-C-O-N N P F H 
|   |    /      |            \ \  | 
F   D   K       D-A-O-L-P-U   T Y E 

反思

这个算法实在是没什么“高端”的地方,本身用了估价函数近似最优,又省略了回溯,可以说简化了太多,但实际上却较好的解决了问题。很多时候我们容易被“最优性”所蒙蔽,苦苦思索最优解而徒劳无功。现实生活中有最优解的情况少之又少,我们应该放眼于一些简单可行的方法,不断想办法优化,一个看似极为复杂的问题,可能被一些十分简单的规律所支配。

时间记录

预计时间 5h 实际用时 5h
代码规范 0.0h   0.0h
具体设计 0.5h   1h
具体编码 3.5h   2h
代码复审 0.0h   0.5h
代码测试 0.5h   1h
测试报告 0.5h   0.5h
posted @ 2013-10-29 01:42  zjoe  阅读(191)  评论(0编辑  收藏  举报