poj-1166
2014-09-10 11:13
162 查看
//3996K 672MS G++ #include <cstdio> #include <cstring> #include <queue> #include <cmath> using namespace std; // int OP[9] = {1+2+8+16, 1+2+4, 2+4+16+32, // 1+8+64, 2+8+16+32+128, 4+32+256, // 8+16+64+128, 64+128+256, 16+32+128+256}; // char OP[9][9] = {"110110000", "111000000", "011011000", // "100100100", "010111010", "001001001", // "000110110", "000000111", "000011010"}; int expv[20]; int move[10][6] = { {0, 0, 0, 0, 0, 0}, {4, 1, 2, 4, 5, 0}, {3, 1, 2, 3, 0, 0}, {4, 2, 3, 5, 6, 0}, {3, 1, 4, 7, 0, 0}, {5, 2, 4, 5, 6, 8}, {3, 3, 6, 9, 0, 0}, {4, 4, 5, 7, 8, 0}, {3, 7, 8, 9, 0, 0}, {4, 5, 6, 8, 9, 0}}; const int MAX = 262250; char visited[MAX]; struct BFSNode { int status; int prevMove; int prevStatus; }; typedef struct BFSNode BFSNode; BFSNode nodeArray[MAX]; typedef queue<int> BFSQueue; BFSQueue bfsQueue; void moveClock(int moveClockId, int & newStatus) { // printf("%d %d\n", moveClockId, newStatus); int bit1, bit2; if(newStatus & expv[2 * (moveClockId - 1)]) bit1 = 1; else bit1 = 0; if(newStatus & expv[2 * (moveClockId - 1) + 1]) bit2 = 1; else bit2 = 0; if(bit1 == 0) newStatus = newStatus + expv[2 * (moveClockId - 1)]; else { if(bit2 == 0) { newStatus = newStatus - expv[2 * (moveClockId - 1)]; newStatus = newStatus + expv[2 * (moveClockId - 1) + 1]; } else { newStatus = newStatus - expv[2 * (moveClockId - 1)]; newStatus = newStatus - expv[2 * (moveClockId - 1) + 1]; } } } void printMove(int curStatus) { if (nodeArray[curStatus].prevStatus == curStatus) { return; } else { printMove(nodeArray[curStatus].prevStatus); if (curStatus) { printf("%d ", nodeArray[curStatus].prevMove); } else { printf("%d\n", nodeArray[curStatus].prevMove); } } } void BFS(int beginStatus) { memset(visited, 0, sizeof(visited)); nodeArray[beginStatus].prevStatus = beginStatus; while(bfsQueue.size()) { bfsQueue.pop(); } bfsQueue.push(beginStatus); visited[beginStatus] = 1; while(bfsQueue.size()) { int curStatus = bfsQueue.front(); // printf("%d\n", curStatus); bfsQueue.pop(); if (curStatus == 0) { printMove(curStatus); return; } for (int i = 1; i <= 9; i++) { int moveStepNum = move[i][0]; int newStatus = curStatus; for (int j = 1; j <= moveStepNum; j++) { moveClock(move[i][j], newStatus); } // printf("newStatus %d\n", newStatus); if (!visited[newStatus]) { visited[newStatus] = 1; nodeArray[newStatus].prevMove = i; nodeArray[newStatus].prevStatus = curStatus; bfsQueue.push(newStatus); } } } } int main() { int beginStatus = 0; int digit = 1; for (int i = 0; i < 9; i++) { int currentVal; scanf("%d", ¤tVal); beginStatus += currentVal*digit; digit *= 4; } expv[0] = 1; for(int i = 1; i < 19; i++) expv[i] = expv[i - 1] * 2; for (int i = 0; i < MAX; i++) { nodeArray[i].status = i; nodeArray[i].prevStatus = -1; } BFS(beginStatus); }
其实算是一道BFS/DFS中阶基础题,思想很简单,不过因为数据的特殊性,会用到一些压缩数据的手法来避免MLE。
这道题其实运用了进制的知识来对状态(就是9个钟表的状态集合)进行压缩。
一开始,抱着侥幸心理,直接开了 10的9次方的状态数组来表示9个钟表的状态(非常浪费,因为这个数组的每一位最多用到3, 后面的 4到9完全浪费了),
果然MLE了。
后来,搜了下,确认进行进制压缩可解,就开做了,因为每一位的数最多从0到3,那么完全可以用 四进制 来唯一的表示某个状态,
比如,某个状态是321321321,唯一对应了一个四进制数,
那么此唯一的四进制转换为唯一的十进制也很简单: 3*pow(4, 0) + 2*pow(4,1) + 1*pow(4, 2) + 3*pow(4, 3) +...+ 1*pow(4, 8)
这样,这些状态中,最大的数也只到pow(4, 9) - 1 = 262143, 那么只需要 262144大小的空间就可以存储BFS的visitflag, 就一定不会MLE。
同时,该题目还要求能将最短BFS路径能打印出来,因为,都是直接在BFSNode里开一个数组来不断的向里面插入新的路径,不过,这种情况下,也可能MLE,因为有可能路径很长,那么路径数组需要开的很大,这样每个bfsQueue中的node都开这么大的数组,不仅浪费,而且可能还会出现路径数组不够大的情况,这次学了一个技巧,要说BFS题做的也挺多了,但还真没这么搞过。
http://blog.csdn.net/bobten2008/article/details/4625624
说白了很简单,既然不在每个node存放至今的路径信息,那么可以将所有的bfsNode事先就开在一个大的数组S里,每个node对应一个status,而由于每个status对应一个数,因此,每个node也唯一对应于S中唯一的一个下标,而因为BFS遍历中,到达每个node的路径都是唯一的,也即每个node的前一个node都是唯一的,那么只需要每个node除了保存当前node对应的staus的number以外,再保存其BFS路径上一个node的下标(其实也是status),以及从上一个node到这个node所作的move操作的类型(1~9)就可以,而bfsQueue中存放的也变为了int型的下标,表示当前BFS的node在S中的位置,对于起始node,
其上一个node的下标可以设为自己或者负数(为了最后递归输出时可以在起始点结束递归), 这样,在最后得到 status == 0时,也就是达成了目标时,就可以从此点的前一个node不断递归,输出相应的move类型即可,虽然一开始要开一个大数组S,但是这种方法很稳妥,不必担心没有足够空间保存路径,并且因为转换过了进制,因此S也不是非常大。
中间还有一个细节就是根据操作类型来调整status的值,因为采用了四进制,所以2个bit保存一个位,为了使用高效的位运算(只能用于二进制), 就对status的相应位拆分为2个二进制位,进行提取和修改,提取出两个bit, bit1 和 bit2, 根据不同组合分别对应 0 1 2 3,而之前的expv数组就排上用场,每个成对应二进制数的某一位,方便进行位操作,根据不同的 0 1 2 3 + 1%4来对status进行调整(code里是一个clock一个clock调整的,好像有更为快捷的矩阵法)。
最后帖一个暴力枚举0s的。。。
原理是利用了,每一种操作,即b[1~9] 在进行超过3次以后就没有意义了(因为会回到远点),
因此来了九重枚举,每一层枚举对相应的操作进行 1 2 3,次, 然后
用当前的状态a[1~9]来加上相应的操作值(比如对于 第一个 clock, 因为能影响第一个clock A的move操作只有1 2 4, 所以是a[1]+b[1]+b[2]+b[4] 然后%4)没这样就求出了在这些次操作以后,a[1]也就是clockA的状态,当9个clock全部为0时,就是题目要求的目的,此时,根据b[1~9]来列出分别进行了多少次的某种类型的操作即可(操作之前的顺序其实不重要,不管以何种顺序进行,结果一样)
#include <string.h> #include <stdio.h> int main() { int i,a[10],b[10],c[10]; for(i=1;i<=9;i++) scanf("%d",&a[i]); for(b[1]=0;b[1]<=3;b[1]++) for(b[2]=0;b[2]<=3;b[2]++) for(b[3]=0;b[3]<=3;b[3]++) for(b[4]=0;b[4]<=3;b[4]++) for(b[5]=0;b[5]<=3;b[5]++) for(b[6]=0;b[6]<=3;b[6]++) for(b[7]=0;b[7]<=3;b[7]++) for(b[8]=0;b[8]<=3;b[8]++) for(b[9]=0;b[9]<=3;b[9]++) { c[1]=(a[1]+b[1]+b[2]+b[4])%4; c[2]=(a[2]+b[1]+b[2]+b[3]+b[5])%4; c[3]=(a[3]+b[2]+b[3]+b[6])%4; c[4]=(a[4]+b[1]+b[4]+b[5]+b[7])%4; c[5]=(a[5]+b[1]+b[3]+b[5]+b[7]+b[9])%4; c[6]=(a[6]+b[3]+b[5]+b[6]+b[9])%4; c[7]=(a[7]+b[4]+b[7]+b[8])%4; c[8]=(a[8]+b[5]+b[7]+b[8]+b[9])%4; c[9]=(a[9]+b[6]+b[8]+b[9])%4; if(c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]+c[8]+c[9]==0) { for(i=0;i<b[1];i++) printf("1 "); for(i=0;i<b[2];i++) printf("2 "); for(i=0;i<b[3];i++) printf("3 "); for(i=0;i<b[4];i++) printf("4 "); for(i=0;i<b[5];i++) printf("5 "); for(i=0;i<b[6];i++) printf("6 "); for(i=0;i<b[7];i++) printf("7 "); for(i=0;i<b[8];i++) printf("8 "); for(i=0;i<b[9];i++) printf("9 "); printf("\n"); return(0); } } }
相关文章推荐
- POJ 1166 敌兵布阵
- poj_1166 The Clocks(高斯消元)
- C语言中的拨钟问题(枚举)(暴力搜索)POJ1166
- POJ 1166 The Clocks (暴搜)
- poj 1166 The Clocks 暴力枚举
- POJ The Clocks 1166
- poj 1166 The Clocks BFS or 高斯消元
- POJ 1166 The Clocks 笔记
- POJ 1166(Gauss消元 取模)
- poj 1166
- POJ 1166 The Clocks(搜索枚举)
- poj 1166 The Clocks 记录路径的广搜
- poj 1166 The Clocks
- poj 1166 The Clocks
- poj 1166 The Clocks
- poj 1166 The Clocks
- POJ 1166 - The Clocks
- poj 1166 敌兵布阵 线段树
- POJ 1166 The Clocks(暴力搜索)
- poj 1166 简单搜索