您的位置:首页 > 其它

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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: