暴力求解法_隐式图搜索(埃及分数,倒水问题,八数码问题)
2017-08-08 11:31
387 查看
隐式图搜索
隐式数的遍历
埃及分数
题目:使用单位分数的和(如1/a,a是自然数)表示一切有理数。例如2/3 = 1/2 + 1/6,但不允许2/3 = 1/3 + 1/3,因为在加数中不允许有相同的。
对于一个分数a/b,表示方法有很多种,其中加数少的比加数多的号,如果加数个数相同,则最小的分数越大越好。例如,19/45 = 1/5 + 1/6 + 1/18是最优方案。
输入整数a,b(0输入:
19/45
输出:
5 6 18
code:
#include<stdio.h> #include<string.h> #include<stack> using namespace std; stack<int> s; stack<int> sbest; //存放最优解 int flag; //标志当前是否得到可行解 int gcd(int m,int n) //求最大公约数 { if(!n) return m; else return gcd(n,m%n); } void minus(int &a,int &b,int c,int d) //分数相减,结果为a/b { int m,n,k; m = a*d - b*c; n = b*d; k = gcd(m,n);//这个起到的作用是化简a/b,消去最大公约数 a = m/k; b = n/k; } int eq(int a,int b,int c,int d) //判断两个分数是否相等 { int m = a*d - b*c; if(m > 0) return 1; else if( m == 0) return 0; else return -1; } void stackCopy(stack<int>& s1,stack<int> s2) //将s2拷贝到s1中 { stack<int> buf; //中转站buf while(!s1.empty()) //将s1清空 s1.pop(); while(!s2.empty()) { buf.push(s2.top()); s2.pop(); } while(!buf.empty()) { s1.push(buf.top()); buf.pop(); } } void dfs(int a,int b,int start ,int depth) { int n; int c,d; int iEqual2,iEqual3; if(depth == 0) return ; /* return用在返回值为void类型的函数中,在执行到某种状态时不需要再执行后续代码了就用return直接结束函数的执行返回至主调函数。 */ else { for(n = start;;n++) { int iEqual = eq(a,b,1,n); if(iEqual == 0) { s.push(n); if(!flag ||sbest.top() > s.top()) //如果当前没有解或者有更好的解 stackCopy(sbest,s); flag = 1; s.pop(); return ; } else if(iEqual > 0) { if((iEqual3 = eq(a,b,depth,n)) >= 0) // a/b > 1/n * depth ?确保能够更新depth层数 return ; s.push(n);//? 说明走到这里的分支是1/n< a/b < 1/n*depth ,这里可以加n放入进去,找到了一个范围 c=a;d=b; minus(c,d,1,n);//a/b-1/n 消去前面已经加上的 dfs(c,d,n+1,depth-1);//这里由于做了减法,使得a/b减小了,在第一层继续寻找答案 s.pop(); //为什么要弹出 } else continue; } } } int main() { int a,b; stack<int> buf; scanf("%d%d",&a,&b); for(int depth = 1;;depth++) { flag = 0; while(!sbest.empty()) sbest.pop(); dfs(a,b,2,depth); if(flag) break; } //出栈 后进显出 while(!sbest.empty()) { buf.push(sbest.top()); sbest.pop(); } //正序输出 while(!buf.empty()) { printf("%d ",buf.top()); buf.pop(); } // scanf("%d",&a); return 0; }
题目:
输入:
输出:
code:
一般隐式图的遍历
倒水问题
题目:有装满水的6升的杯子,空的3升杯子和1升的杯子,3个杯子中都没有刻度。在不使用其他道具的情况下,是否可以量出4升的水呢?
采用bfs宽度优先遍历,开始看不懂了
输入:
2 6 4 1 3 7 0 5 8 8 1 5 7 3 6 4 0 2
输出:
31
code:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 1000000 typedef int State[9]; /*s的数据类型是长度为100的数组,数组元素s[i]的数据类型是State是长度为10的数组,等同于定义了一个二维数组s[100]=iarr[100][9]. int iarr[100][9],arr的数据类型是长度为100的数组,数组元素是arr[i],arr[i]的数据类型是长度为10的数组. */ State st[MAXSIZE];//状态数一定要多定义,否则一不小心就超了 State goal; int iDist[MAXSIZE];//距离数组 int go[][2] =//上下左右方向的数组 {{-1,0},{1,0},{0,-1},{0,1}}; int iVis[362880],fact[9];//9!=362880,8!=40320,9*8!=9!共有这么多排序,然后我们寻找,我们初始化fact void init()//初始化查找表 { fact[0] = 1; for(int i = 1 ; i < 9; i++) { fact[i] = fact[i-1]*i; } } //bool isInsert(State state) bool isInsert(int n)//去重,采用编码与解码机制,确保一个9维状态只能映射到一个数字,并且映射的数字最大值不能超过9! { int iCode = 0;//编码值 for(int i = 0 ; i < 9 ; i++) { int iCnt = 0; for(int j = i+1; j < 9;j++) { if(st [j] < st [i])//统计每个排列中,后面小于前面排列的数字个数 { iCnt++;} } iCode += fact[8-i]*iCnt; } if(iVis[iCode])//如果已经访问过 { return false; } else { iVis[iCode] = 1; return true;//同时完成赋值和返回值操作 } } int bfs() { int rear = 2,front = 1; init();//这里进行判重,对于树不需要判断重复。但是对于图需要判断 while(front < rear) { State& state = st[front]; if(memcmp(goal,state,sizeof(state)) == 0)//判断是否找到的工作要放在开头 { return front; } int iZ,iX,iY; for(int i = 0 ; i < 9; i++)//确定0所在的位置 { if(!state[i]) { iZ = i; iX=iZ/3;iY=iZ%3; break;//凡是寻找类的问题,一旦找到,必须用break跳出 } } //生成下一步位置 int newz,newx,newy; for(int i = 0; i < 4; i++) { newx = go[i][0] + iX; newy = go[i][1] + iY; newz = newx*3 + newy;//确定0的新位置 if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3)//剪枝 { State& newState = st[rear];//这里应该从队尾提前将原来老的状态拷贝给新的状态,再将新状态中需要修改0元素的地方进行修改,需要用引用,为修改做准备 memcpy(&newState,&state,sizeof(state)); newState[newz] = state[iZ];//新矩阵0元素的位置上放0元素 newState[iZ] = state[newz];//新矩阵原来放0元素的位置上现在放上新生成的0元素的坐标,这里必须用原来被交换元素的值替换 iDist[rear] = iDist[front] + 1;//更新移动的步数 } if(isInsert(rear))//修改队尾指针 { rear++; } } front++;//不管是否成功,修改队头 } return 0; } void process() { //初始化队头和队尾元素 for(int i = 0 ; i < 9;i++) { scanf("%d",&st[1][i]); } for(int j = 0 ; j < 9; j++) { scanf("%d",&goal[j]); } iDist[1] = 0;//设置第一步移动的距离为0 memset(iVis,0,sizeof(iVis));//初始化访问内存块,就是这句话没加导致错误的 } int main(int argc,char* argv[]) { process(); int ans = bfs();//返回的是front的值,但不是移动次数,移动次数得用dist来计算,因为这里是宽度优先搜索,如果用front的值,那么中间尝试的节点也算了 if(ans > 0) printf("%d\n",iDist[ans]); else printf("-1\n"); return 0; }
相关文章推荐
- 暴力求解法_隐式图搜索(八数码问题)
- 埃及分数问题(迭代加深搜索)
- 【算法学习笔记】17.暴力求解法05 隐式图搜索1 迭代加深搜索 埃及分数
- 白书学习之隐式图搜索之八数码问题
- 埃及分数问题 迭代加深搜索(IDDFS)
- UVa 10603 - Fill,经典倒水问题+隐式图搜索+dfs
- 埃及分数问题(剪枝+迭代加深搜索)=>算法竞赛入门经典(第二版)第七章
- 【算法学习笔记】17.暴力求解法05 隐式图搜索1 迭代加深搜索 埃及分数
- 埃及分数问题 迭代加深搜索
- UVA10603倒水问题+隐式图搜索
- 埃及分数问题-迭代加深搜索与IDA*算法
- uva 10603倒水问题(搜索 隐式图的最短路 )
- UVA_10603 倒水问题 隐式图搜索
- 【迭代加深搜索】埃及分数问题
- [迭代加深搜索] Codevs1288 埃及分数问题
- 【算法学习笔记】18.暴力求解法06 隐式图搜索2 八数码问题 未启发
- 算法竞赛入门经典:第七章 暴力求解法 7.18 广度优先搜索之八数码问题
- [隐式图搜索]Fill(倒水问题) UVA10603
- 小白书隐式图搜索之八数码问题
- 【算法学习笔记】18.暴力求解法06 隐式图搜索2 八数码问题 未启发