zoukankan      html  css  js  c++  java
  • ACM:回溯,八皇后问题,素数环

    (一)八皇后问题
    (1)回溯

    #include <iostream>
    #include <string>
    
    #define MAXN 100
    
    using namespace std;
    
    int tot = 0, n = 8;
    int C[MAXN];
    
    void search(int cur) {
    	if(cur == n) ++tot;       //递归边界,仅仅要走到了这里。全部皇后必定不冲突
    	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);     //假设合法,则继续递归
    	}
    }
    
    int main() {
    	search(0);
    	cout << tot << endl;
    	return 0;
    }
    



    (2)利用二维数组优化的回溯法

    #include <iostream>
    #include <string>
    
    #define MAXN 100
    
    using namespace std;
    
    int tot = 0, n = 8;
    int vis[3][MAXN], C[MAXN];    
    
    void search(int cur) {
    	if(cur == n) ++tot;
    	else for(int i = 0; i < n; ++i) {
    		if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {    //利用二维数组直接推断
    			C[cur] = i;
    			vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;   //改动全局变量
    			search(cur+1);
    			vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;   //这里一定要改回来!
    		}
    	}
    }
    
    int main() {
    	memset(vis, 0, sizeof(vis));
    	search(0);
    	cout << tot << endl;
    	return 0;
    }
    

    在上面的程序中,vis数组表示已经放置的皇后占领了哪些列、主对角线和副对角线。

    一般的在回溯法中,假设改动了全局变量vis数组,那么递归调用结束后一定要改动回来!由于在解答树中,假设下一层不满足条件,那么就须要回溯。那么就要把改动过的vis给改回来,那样,才干继续进行下一次的推断!!!


    (二)素数环

    题目:输入正整数n,把整数1。2,3,...,n组成一个环。使得相邻两个整数之和均为素数。

    输出时从整数1開始逆时针排列。

    同一个环应该恰好输出一次。

    典型的回溯法,代码例如以下:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 1000;
    int isp[MAXN], vis[MAXN], A[MAXN], n;
    
    int is_prime(int x) {    //推断一个数是否为素数
    	for(int i = 2; i*i <= x; ++i) {
    		if(x % i == 0) return 0;
    	}
    	return 1;
    }
    
    void dfs(int cur) {
    	if(cur == n && isp[A[0] + A[n-1]]) {
    		for(int i = 0; i < n; ++i) cout << A[i] << " ";
    		cout << endl;
    	}else {
    		for(int i = 2; i <= n; ++i) {
    			if(!vis[i] && isp[i + A[cur-1]]) {
    				A[cur] = i;   //数字i满足条件,所以第cur个位置能够放数字i
    				vis[i] = 1;
    				dfs(cur+1);
    				vis[i] = 0;   //跟上题一样。一定不能忘记把vis的值改回来,原因见上一题的代码凝视
    			}
    		}
    	}
    }
    
    int main() {
    	memset(vis, 0, sizeof(vis));   //递归调用之前,一定要把vis函数清0
    	cin >> n;
    	for(int i = 2; i <= 2*n; ++i) isp[i] = is_prime(i);   //推断一个数不是质数。为了方便后推断
    	A[0] = 1;   //从标题中的规定的第一个数字1开始
    	dfs(1);     //所以递归调用位置的1开始,而不是从位置0开始,因为数字第一位置已被确定是1
    	return 0;
    }





  • 相关阅读:
    WCF异常管理—不要在using语句中调用WCF服务(z)
    正则表达式
    nginx汇总(z)
    WCF编解码实现
    zTree -- jQuery 树插件
    C# 程序异常管理方案
    WPF剪切板问题-OpenClipboard HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN))
    乘法逆元
    二叉树后序遍历(非递归)
    P1892 [BOI2003]团伙
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5037127.html
Copyright © 2011-2022 走看看