DFS的应用

    xiaoxiao2021-03-26  20

    题目1 : 九宫

    时间限制: 10000ms 单点时限: 1000ms 内存限制: 256MB

    描述

    小Hi最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将1~9不重复的填入一个3*3的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。

    三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:“二四为肩,六八为足,左三右七,戴九履一,五居其中”,通过这样的一句口诀就能够非常完美的构造出一个九宫格来。

    有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小Hi准备将一个三阶幻方(不一定是上图中的那个)中的一些数组抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一组解。

    而你呢,也被小Hi交付了同样的任务,但是不同的是,你需要写一个程序~

    输入

    输入仅包含单组测试数据。

    每组测试数据为一个3*3的矩阵,其中为0的部分表示被小Hi抹去的部分。

    对于100%的数据,满足给出的矩阵至少能还原出一组可行的三阶幻方。

    输出

    如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出“Too Many”(不包含引号)。

    样例输入 0 7 2 0 5 0 0 3 0

    此题本人未能独立解出,参考博客用户(lizhaowei213)(侵删)所给解法后,获益匪浅。

    在此的解法利用DFS算法。DFS,深度优先搜索,对于多层/多阶的结构,DFS能够依次地,由深到浅,不重复地尝试每一种情况。深度优先,故名思义,从最深的位置开始,依次尝试每一种可能的值,代码表现为:

    elemtype dfs(){ ... ... if(情况1){ for(对于所有条件){ ... //将其设置为已经遍历过 dfs(); //递归的调用dfs } } else if(情况2....){ } }

    下面给出此题代码:

    #include<stdio.h> #include<string.h> #define MAXLEN 10 //define some global variables //对于简单的算法题,如果一些变量需要经常在函数中使用,不妨设为全局变量 int graph[MAXLEN],visited[MAXLEN],temp[MAXLEN];//we dont use the first elem int flag=0; int Judge(){ int sum; sum=graph[1]+graph[2]+graph[3]; if(graph[1]+graph[4]+graph[7]!=sum) return 0; if(graph[1]+graph[5]+graph[9]!=sum) return 0; if(graph[2]+graph[5]+graph[8]!=sum) return 0; if(graph[3]+graph[6]+graph[9]!=sum) return 0; if(graph[3]+graph[5]+graph[7]!=sum) return 0; if(graph[4]+graph[5]+graph[6]!=sum) return 0; if(graph[7]+graph[8]+graph[9]!=sum) return 0; return 1; } void DFS(int pos){ int i; if(pos==10&&Judge()){ //is ok flag++; if(flag==1) memcpy(temp,graph,sizeof(graph)); //此处学到memcpy函数的用法 return; } if(graph[pos]){ //has a value now DFS(pos+1); } else{ for(i=1;i<=9;i++){ if(visited[i]){ //the number has been used continue; } visited[i]=1; graph[pos]=i; DFS(pos+1); //如果回溯到此,则说明较深处递归的尝试(填进去的数)的都是不满足条件的,

    //那么就要重置归“零”本轮的尝试(填进去的数),以便不影响下一次的尝试。 visited[i]=0; graph[pos]=0; } } } int main(){ int i; memset(visited,0,sizeof(visited)); //此处学到memset函数的用法 for(i=1;i<=9;i++){ scanf("%d",&graph[i]); visited[graph[i]]=1; } DFS(1); if(flag==1){ for(i=1;i<=9;i++){ printf("%d%c",temp[i],i%3==0?'\n':' '); //此处学到一个格式化输出的小技巧 } } else if(flag>1) { printf("Too Many\n"); } else printf("wrong!\n"); return 0; }

    另多言几句,根据此题在下学习到了做算法题能实实在在的帮助我更真切的理解数据结构,以及编程语言的特性。

    ————————————————————————————分割线————————————————

    又一关于DFS的题目

    集合的幂集

    Time Limit:1000MS  Memory Limit:65536K Total Submit:453 Accepted:209

    Description

    设S是有n(n≤20)个元素的集合,S的幂集是S所有可能的子集组成的集合。例如,S={a,b,c},则S的幂集={()(c)(b)(bc)(a)(ac)(ab)(abc)}。写一个C++递归程序,以S为输入,输出S的幂集。

    Input

    n(n≤20)的值以及S的n个元素

    Output

    S的幂集

    Sample Input

    3 abc

    Sample Output

    ()(c)(b)(bc)(a)(ac)(ab)(abc)

    解体思路:

    此题可转化其义为:遍历所有的情况,即对于所有集合元素,都有取或者不取两种情况。

    那么此时可以看出DFS的用处何在:

    1.对于路径问题,每一步都有两种选择:走 / 不走    

    2.对于组合问题,每一个元素也有两种选择:取 / 不取    

    DFS可以遍历“所有的选择的组合”

    很多问题都可以转化成路径问题(由外到内/由浅到深的关系)或组合问题(取或者不取)。

    下面是源码:

    #include<stdio.h> char a[20]; int b[20]; void DFS(int t,int n){ int i,j; if(t<n){ //当t有意义的时候 for(i=0;i<2;i++){ b[t]=i; DFS(t+1,n); } } else { printf("("); for(j=0;j<n;j++){ if(b[j]==1) printf("%c",a[j]); } printf(")"); } } int main() { int n; int i; scanf("%d",&n); scanf("%s",a); for(i=0;i<n;i++) { b[i]=0; } DFS(0,n); printf("\n"); }

    转载请注明原文地址: https://ju.6miu.com/read-658769.html

    最新回复(0)