【算法】算法的应用(三)

八皇后问题

   求八皇后问题所有的解。   实例解析:   这是一个非常古老的问题:如何在一个8*8的棋盘上无冲突地放置8个皇后棋子,称为八皇后问题。    在国际象棋中,皇后可以沿着任何直线和任何45°斜线移动吃掉别的棋子,因此,任何一个皇后所在的横线上、竖线上和两条对角线上都不能有其他皇后存在。一个完整的、无冲突的八皇后分布称为八皇后问题的一个解。本例求解八皇后的所有解。   很显然,棋盘的每一行都必须有且只能有一个皇后,因此我们从第一行开始,先放下一个皇后(有8个位置可以放置),然后用递归的方式调用函数本身,去放置后面的棋子,直到第八行放完,则表示找到了一个解。   对于每一行,可以放置皇后的位置有8个,可用循环的方式来逐个试探,若无冲突,则放置棋子,继续下一层递归调用……,若冲突,则换本行另一个位置。   下面为程序代码:

#include <math.h>
#include <stdio.h>
#define  MAX  8         //棋盘大小MAX*MAX
int board[MAX];   
int count = 0;
void show_result()          //输出结果
{
    int i;
    for(i = 0; i < MAX; i++)
        printf("(%d,%d),", i+1, board[i]+1);  //生活中从1开始计数
    printf("\n");
}
  
int check_cross(int n)     //检查是否与前面已经存在的皇后冲突
{
    int i;
    for(i = 0; i < n; i++)
        if(board[i]==board[n]||(n-i)==abs(board[i]-board[n]))
        return 1;              //若冲突返回1
    return 0;
}
void put_chess(int n)          // 在第n行放棋子
{
    int i;
    for(i = 0; i < MAX; i++)
    {   //依次对第0~7列试探有无冲突
        board[n] = i;                // board[n]存储列号 (行号是n)
        if(!check_cross(n))
        {       //不冲突
            if(n == MAX-1)
            {         //若已经到第八行,则找到一个解
                count++;
                printf(“%3d: ”, count);  //输出一个解的序号
                show_result();            //输出結果
                if(count%24 == 0)
                {       //每24行一屏暂停
                    getch();
                    clrscr();
                }
            }
        else
            put_chess(n+1);   //若未到第八行,则递归调用,进入下一行
        }
    }
}
  
int main()
{
    clrscr();
    puts("The possible placements are:");
    put_chess(0);
    puts("\n Press any key to quit...");
    getch();
    return 0;
}

  上面使用的是递归算法,还可以使用一种非递归回溯的算法

#define TRUE  1
#define FALSE 0
int nQueens_nonrecursive(int *a, int n)
{
    int top, i, j, conflict;
    if(n <= 0)
        return FALSE;
    top = -1;
    i = 0;
    do
    {
        conflict = FALSE;
        /*判断会不会与前面已经放置的发生冲突*/
        for(j = 0; j < top+1; j++)
            if(i==a[j]||top+1-j==i-a[j]||top+1-j==a[j]-i)
                conflict = TRUE;
        if(conflict == FALSE)
        {  //如果不冲突
            a[++top] = i;          //把当前皇后放到第i列
            if(top == n-1)
                       return TRUE;        //问题已成功解决
            i = 0;                  //从下一行的第0列开始,继续试探
        }
        else
        {                      //如果冲突
            while(i == n-1 && top >= 0)
                       i = a[top--];
            i++;
        }
    }
    while(i < n);
    return FALSE;
}

   这里只列出了部分函数    
迷宫问题    从迷宫中找出从入口到出口的所有通道。     实例解析:   迷宫可用图17-6所示的方块来表示,每个方块或为通道(以空白方块表示)或为墙(以带阴影的方块表示)。要求找到一条从入口到出口的简单路径,即在求得的路径上不能重复出现同一通道块。   求解迷宫问题的简单方法是:从入口出发,沿某一方向进行搜索,若能走通,则继续向前走;否则沿原路返回,换一方向再进行搜索,直到所有可能的通路都搜索到为止。   在计算机中可用图17-7所示的二维数组maze[m][n]来表示,数组中元素为0表示通道,为1表示墙。对其中的任一点maze[i][j],可能的运动方向有4个。回溯法求迷宫问题解的过程要用一个栈来保存搜索的路径。     核心代码如下:

#include <stdio.h>
#include <stdlib.h>
#define M 8         // maze数组的行数
#define N 11        // maze数组的列数
typedef struct
{
    int x,y,d;
}DataType;
struct  SeqStack
{  //顺序栈类型定义
    int MAXNUM;
    int  t;         // t<MAXNUM,指示栈顶位置,而不是元素个数
    DataType  *s;
};
          
typedef  struct SeqStack  *PSeqStack; //顺序栈类型的指针类型
PSeqStack CreateEmptyStack_seq(int  n);
void  push_seq( PSeqStack pastack, DataType x );
void  pop_seq( PSeqStack pastack );
int isEmptyStack_seq(PSeqStack pastack);
DataType  top_seq( PSeqStack pastack );
/*下面函数求从入口maze[x1][y1]到出口maze[x2][y2]的一条路径,其中 1<=x1, x2<=M-2 , 1<=y1, y2<=N-2 */
void mazePath(int** maze, int direction[4][2],int x1,int y1,int x2,int y2,int m,int n)
{
    int i,j,k;
    int g,h;
    PSeqStack  st;
    DataType element;
    st = CreateEmptyStack_seq(m*n);
        if(st == NULL)
            return;
    maze[x1][y1] = 2;         //从入口开始进入, 作标记
    element.x = x1;
    element.y = y1;
    element.d = -1;
    push_seq(st,element);              //入口点进栈
    while(!isEmptyStack_seq(st))
    {    //走不通时, 一步步回退
        element = top_seq(st);
        pop_seq(st);
        i = element.x;
        j = element.y;
        k = element.d + 1;
        while(k <= 3)
        {                   //依次试探每个方向
            g = i + direction[k][0];
            h = j + direction[k][1];
            if(g==x2 && h==y2 && maze[g][h]==0)
            { //走到出口点
                printf("The revers path is:\n");  //打印路径上的每一点
                printf("the node is: %d %d \n",g,h);
                printf("the node is: %d %d \n",i,j);
                while(!isEmptyStack_seq(st))
                {
                    element = top_seq(st);
                    pop_seq(st);
                    printf("the node is: %d %d \n",element.x,element.y);
                }
                free(st->s);
                free(st);
                return;
            }
            if(maze[g][h] == 0)
            {   //走到没走过的点
                maze[g][h] = 2;     //作标记
                element.x = i;
                element.y = j;
                element.d = k;
                push_seq(st,element);  //进栈
                i = g;     //下一点转换成当前点
                j = h;
                k = -1;
            }
            k = k + 1;
        }
    }
    printf("The path has not been found.\n");
        free(st->s);
    free(st);
}
  
int main()
{
    int maze[M][N] ={
                    {1,1,1,1,1,1,1,1,1,1,1},
                    {1,0,1,0,0,1,1,1,0,0,1},
                    {1,0,0,0,0,0,1,0,0,1,1},
                    {1,0,1,1,1,0,0,0,1,1,1},
                    {1,0,0,0,1,0,1,1,0,1,1},
                    {1,1,0,0,1,0,1,1,0,0,1},
                    {1,1,1,0,0,0,0,0,0,0,1},
                    {1,1,1,1,1,1,1,1,1,1,1},
                    };
    int direction[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
    mazePath(maze,direction,1,1,6,9,M,N);
    return 0;
}

   
表达式计算    对于键盘输入的表达式,判断是否合法,如果合法,输出运算结果。   说明:    (1)表达式不能为空    (2)可以出现在表达式中的字符有:    运算符“+”、“-”、“*”、“\”;    左右括号“(”、“)”;    整数(可以是多位的);    空格和制表符。   例如:若输入的表达式为“20 + (3*(4+1) – 5)/2 – 3”,则应输出22。    实例解析:    使用栈先将中缀表达式转化为后缀表达式,再来求后缀表达式的值。    1、转化为后缀表达式步骤如下: 建一个空栈作为运算符栈,顺序扫描中缀表达式。   ①若读到其他字符:忽略。其他字符包括‘  ’、 ‘ \t’等。    ②若读到数字:输出(指:写入数组)    ③若读到左括号:入栈    ④若读到右括号:不断将栈中元素弹出、输出,直到遇到左括号,将其弹出但不输出。     ⑤若读到加或减:反复检查,若栈不空且栈顶为加、减、乘或除时,弹出并输出栈顶元素。读入的运算符(加或减)入栈。    ⑥若读到乘或除:若栈不空且栈顶为乘或除时,弹出并输出栈顶元素。读入的运算符(乘或除)入栈 缀表达式扫描完毕后,将栈中剩余元素弹出、输出。    2、计算后缀表达式值步骤如下:    创建一个空栈,顺序扫描后缀表达式    ①若读到数字:入栈。    ②若读到其他字符:忽略。其他字符包括‘  ’、 ‘ \t’等。    ③若读到运算符:从栈中弹出两个元素进行计算,将结果入栈。    后缀表达式扫描完毕后,栈顶元素即为结果。    程序如下:

#include <stdio.h>
#include <malloc.h>
#define DataType char
#define TRUE  1
#define FALSE 0
#define MAX_SEQSTACK 100
struct  SeqStack
 {    //顺序栈类型定义
    int MAXNUM;
    int  t;           // t<MAXNUM,指示栈顶位置,而不是元素个数
    DataType  *s;
};
      
typedef  struct SeqStack  *PSeqStack; //顺序栈类型的指针类型
/以下5个函数的实现见本章实例13
PSeqStack CreateEmptyStack_seq();
void  push_seq(PSeqStack pastack, DataType x);
void  pop_seq(PSeqStack pastack);
int isEmptyStack_seq(PSeqStack pastack);
DataType  top_seq(PSeqStack pastack);
//下面代码将中缀表达式转换为后缀表达式,如成功返回TRUE
int infixtoSuffix(char *infix, char *suffix)
{
    int state_int = FALSE;
    /*记录状态,TRUE表示刚读入的是数字,FALSE表示刚读入的不是数字。  设置这个变量的目的是每输出一个整数后输出一个空格,以免连续输出的两个整数混在一起。*/
    char c, c2;
    int i, j = 0;
    PSeqStack ps = CreateEmptyStack_seq();
       if(ps == NULL)
             return FALSE;       
    if(infix[0] == '\0')
    {
        free(ps->s);
        free(ps);
        return FALSE;
    }
    for(i = 0; infix[i] != '\0'; i++)
    {
        c = infix[i];
        switch(c){
        case ' ':
            case '\t':
            case '\n':
                if(state_int == TRUE) //从TRUE到FALSE时,输出空格
                    suffix[j++] = ' ';
                state_int = FALSE;
            break;
        case '0':
            case '1':
            case '2':
            case '3':
            case '4':
        case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                state_int = TRUE;
                suffix[j++] = c;      //遇到数字,输出
            break;
        case '(':
            if(state_int == TRUE)
                suffix[j++] = ' ';
            state_int = FALSE;
            push_seq(ps, c);
            break;
        case ')':
            if(state_int == TRUE)
                suffix[j++] = ' ';
            state_int = FALSE;
            c2 = ')';
            while(!isEmptyStack_seq(ps))
            {
                c2 = top_seq(ps);
                pop_seq(ps);
                if(c2 == '(')
                    break;
                suffix[j++] = c2;
            }   //读入右括号,弹出输出,直到遇到左括号,弹出不输出
            if(c2 != '(')
            {
                free(ps->s);
                free(ps);
                suffix[j++] = '\0';
                return FALSE;  //找不到左括号,非法
            }
            break;
        case '+':
            case '-':
            if(state_int == TRUE)
                suffix[j++] = ' ';
            state_int = FALSE;
            while(!isEmptyStack_seq(ps))
            {
                c2 = top_seq(ps);
                if(c2=='+' || c2=='-' || c2=='*' || c2=='/')
                {
                    pop_seq(ps);
                    suffix[j++] = c2;
                }        //栈顶为加减乘除时,弹出栈顶元素并输出
                else
                    break;
            }
            push_seq(ps,c);
            break;
        case '*':
            case '/':
            if(state_int == TRUE)
                suffix[j++] = ' ';
            state_int = FALSE;
            while(!isEmptyStack_seq(ps))
            {
                c2 = top_seq(ps);
                if(c2 == '*' || c2 == '/' )
                {
                    pop_seq(ps);
                    suffix[j++] = c2;
                }   //栈顶为加减乘除时,弹出栈顶元素并输出
                else
                    break;
            }
            push_seq(ps,c);
            break;
        default:             //出现其他非法字符
            free(ps->s);
            free(ps);
            suffix[j++] = '\0';
            return FALSE;
        }
    }
    if(state_int == TRUE)
        suffix[j++] = ' ';
    while(!isEmptyStack_seq(ps))
    {
        c2 = top_seq(ps);
        pop_seq(ps);
        if(c2 == '(')
        {
            free(ps->s);
            free(ps);
            suffix[j++] = '\0';
            return FALSE;
        }
        suffix[j++] = c2;
    }
    free(ps->s);
       free(ps);
    suffix[j++] = '\0';
    return TRUE;
}
/*下面函数用于计算后缀表达式的值,若非法返回FALSE;否则返回TRUE,且*presult存放计算结果*/
int calculateSuffix(char *suffix, int *presult)
{
    int state_int = FALSE;
    int num = 0, num1, num2;
    int i;
    char c;
    PSeqStack ps = CreateEmptyStack_seq();
    if(ps == NULL)
        return FALSE;      
    for(i = 0; suffix[i] != '\0'; i++)
    {
        c = suffix[i];
        switch(c)
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            if(state_int == TRUE)
                num = num*10 + c - '0';
            else
                num = c -'0';
            state_int = TRUE;
            break;
        case ' ':
        case '\t':
        case '\n':
            if(state_int == TRUE)
            {
                push_seq(ps, num);
                state_int = FALSE;
            }
            break;
        case '+':
        case '-':
        case '*':
        case '/':
            if(state_int == TRUE)
            {
                push_seq(ps, num);
                state_int = FALSE;
            }
            if(isEmptyStack_seq(ps))
            {
                free(ps->s);
                free(ps);
                return FALSE;
            }
            num2 = top_seq(ps);
            pop_seq(ps);
            if(isEmptyStack_seq(ps))
            {
                free(ps->s);
                free(ps);
                return FALSE;
            }
            num1 = top_seq(ps);
            pop_seq(ps);
            if(c == '+')
                push_seq(ps, num1+num2);
            if(c == '-')
                push_seq(ps, num1-num2);
            if(c == '*')
                push_seq(ps, num1*num2);
            if(c == '/')
            {
                if(num2 == 0)
                {    //除数为0返回FALSE
                    free(ps->s);
                    free(ps);
                    return FALSE;
                }
                push_seq(ps, num1/num2);
            }
            break;
        default:              //出现其他非法字符
            free(ps->s);
                free(ps);
            return FALSE;
        }
    }
    *presult = top_seq(ps);
    pop_seq(ps);
    if(!isEmptyStack_seq(ps))   //栈中还有其他字符,非法
    {
        free(ps->s);
        free(ps);
        return FALSE;
    }
    free(ps->s);
        free(ps);
        return TRUE;
}
  
int main()
{
    char infix[80] = "20+(3*(4+1)-5)/2-3";
    char suffix[80];
    int result;
    if(infixtoSuffix(infix,suffix) == TRUE)
    {
        if(calculateSuffix(suffix,&result) == TRUE)
            printf("The Reuslt is: %3d\n", result);
        else
            printf("Error!\n");
    }
    else
        printf("Input Error!\n");
    return 0;
}

     

本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1171355

    原文作者:Leo.cheng
    原文地址: https://www.cnblogs.com/lcw/p/3159445.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞