【回溯法】poj1321/zoj1002/八皇后

欢迎指正!

先放几个相关链接:

1.《浅谈回溯法与深度优先搜索》

2.  poj1321 题目链接

3.  zoj1002 题目链接

4. 八皇后问题

八皇后问题通常被当作回溯法的典型案列。【回溯法】,其实换一个通俗易懂的名字,其实就是<递归枚举>,再说得通俗一点,其实就是一种<暴力>。  因此,这种解法时间耗费其实是指数级增长。基于此,题目中的N(递归树、解答树层数?)一般不会给太大。

知乎上有这样一个有个话题,叫《如何用十行代码搞定八皇后》。刘汝佳那本算法书上就是十行搞定的。哇,感觉这么经典的十行代码,不把它记在心里真是有点可惜了:

void search(int cur){
   if(cur==n) ans++;
   else 
    for(int i=0;i<n;i++){
       int ok=1;
      C[cur]=i;//尝试把cur行的皇后放在第i列
  for(int j=0;j<cur;j++)
       if(C[cur]==C[j]||cur-C[cur]==j-C[j]||cur+C[cur]==j+C[j]){
   ok=0;break;
}
      if(ok) search(cur+1);
   }
}



zoj1002代码:

【ps:这道题由于求得是碉堡最多可以放置多少个,这就让我一开始尝试用贪心法去解答。但是结果案例四会出错,细想一下,发现这条路走不通(打补丁都补不住)】

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

int n;
int ans;
char mmap[4][4];

int isOK(int x,int y)
{

    int caution=0;
    for(int j=0; j<y; j++)
    {
         if(mmap[x][j]=='*')
            caution=1;
        if(mmap[x][j]=='X')
            caution=0;

    }
    if(caution)
        return 0;
    else
    {
        for(int i=0; i<x; i++)
        {
            if(mmap[i][y]=='*')
                caution=1;
            if(mmap[i][y]=='X')
                caution=0;
        }
        if(caution)
            return 0;
        else
            return 1;
    }
}

//将所有的位置从0到n*n-1标号。当前位置为k,当前已经摆放m个碉堡
void dfs(int k,int m)
{
   // cout<<"hahah  "<<"k="<<k<<"  m="<<m<<"\n";
    if(k==n*n) //遍历完所有位置
    {
     //   cout<<"m="<<m<<"\n";
        if(m>ans)
            ans=m;
        return;
    }
    else
    {
        int x=k/n;
        int y=k%n;
 //     cout<<"x="<<x<<" "<<"y="<<y<<"\n";
        if(mmap[x][y]=='.'&&isOK(x,y))
        {
            mmap[x][y]='*';//摆上碉堡
            dfs(k+1,m+1);
            mmap[x][y]='.';//递归返回时一定要恢复原状
        }
        dfs(k+1,m);
    }
}



int main()
{
    while(~scanf("%d",&n)&&n!=0)
    {
        ans=0;
        for(int i=0; i<n; i++)
            scanf("%s",&mmap[i]);
        dfs(0,0);
        cout<<ans<<"\n";
    }
    return 0;
}



poj1321:

这道题做了有点时间了,不过做上面zoj1002的时候马上就想到它了。这道题比zoj1002更接近八皇后原题。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[10][10];     //记录棋盘位置
int book[10];        //记录一列是否已经放过棋子
int n,k;
int total,m;    //total 是放棋子的方案数 ,m是已放入棋盘的棋子数目

void DFS(int cur)
{
    if(k==m)
    {
        total++;
        return ;
    }
    if(cur>=n)    //边界
        return ;
        
    for(int j=0; j<n; j++)
        if(book[j]==0 && a[cur][j]=='#')  //判断条件
        {
            book[j]=1;           //标记
            m++;                 
            DFS(cur+1);
            book[j]=0;           //第cur可以放 ,也可以不放,改回来的目的就是 
            m--;                 // 到29行,判断cur可以放但是不放的情况 
        }
    DFS(cur+1);                //到下一行
}

int main()
{
    int i,j;
    while(scanf("%d%d",&n,&k)&&n!=-1&&k!=-1) //限制条件
    {
        total=0;
        m=0;
        for(i=0; i<n; i++)
            scanf("%s",&a[i]);
        memset(book,0,sizeof(book));
        DFS(0);
        printf("%d\n",total);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/k0_0k/article/details/79966887