POJ 1166 The Clocks [BFS] [位运算]

1.题意:有一组3*3的只有时针的挂钟阵列,每个时钟只有0,3,6,9三种状态;对时针阵列有9种操作,每种操作只对特点的几个时钟拨一次针,即将时针顺时针波动90度,现在试求从初试状态到阵列全部指向0的状态所需要的最小操作数的操作方案;

2.输入输出:输入给出阵列初始状态,0,1,2,3分别表示0,3,6,9;要求输出最快方案的操作序列;

3.分析:IOI 1994的考题,BFS是比较容易想到的方法之一,关键是如何简洁的表示和改变BFS过程中的阵列状态;这里使用位运算的方法;具体如下:

首先一共9个时钟,每个时钟有0,1,2,3这4种状态,所以每个时钟可以由两位二进制数表示,则整个阵列就可以用18位二进制数表示,这里表示的问题就解决了;下一步就是如何表示"转动90度了",这里分三步,1)从状态中取出要调整的那个钟,2)对取出的sub部分进行模拟转动90度的操作,3)将处理完的sub放回原状态;经过这样的三个步骤之后,对一个钟的修改就完成了,针对每种操作,对这个操作内的每个时钟进行修改,就对当前状态完成了操作;

综上所述,这样的位运算方法使得表示更加简洁,计算的更快;具体代码如下:

 1 # include <cstdio>
 2 # include <iostream>
 3 # include <queue>
 4 # include <cstring>
 5 # include <stack>
 6 using namespace std;
 7 const int MAXN=1<<18;
 8 int clock[10];
 9 int vis[MAXN];
10 struct Node
11 {
12     int status,move,pre;
13     Node (){}
14     Node(int ss,int mm,int pp)
15     {
16         status=ss;
17         move=mm;
18         pre=pp;
19     }
20 }L[MAXN];
21 int M[9][5]=
22 {{0,1,3,4,-1},{0,1,2,-1,-1},{1,2,4,5,-1},
23  {0,3,6,-1,-1},{1,3,4,5,7},{2,5,8,-1,-1},
24  {3,4,6,7,-1},{6,7,8,-1,-1},{4,5,7,8,-1}};
25 void Init()
26 {
27     for(int i=0;i<9;i++)
28         scanf("%d",&clock[i]);
29     memset(vis,0,sizeof(vis));
30 }
31 void Solve()
32 {
33     queue<Node> Q;
34     int start=0;
35     int len=0;
36     for(int i=0;i<9;i++)
37         start|=(clock[i]<<(i<<1));
38     Q.push(Node(start,-1,-1));
39     vis[start]=1;
40     while(!Q.empty())
41     {
42         Node temp=Q.front();
43         Q.pop();
44         L[len++]=temp;
45         if(temp.status==0)
46             break;
47         //printf("%d
",temp.move);
48         for(int i=0;i<9;i++)
49         {
50             int nclock=0;
51             int nstatus=temp.status;
52             for(int j=0;j<5;j++)
53                 if(M[i][j]>=0)
54                 {
55                     nstatus&= ((1<<18)-1) ^ (3<<(2*(M[i][j])));
56                     //"1111[目标位]1111",把待处理部分位的位置清零 
57                     nclock=(temp.status >> (2*(M[i][j]))) & 3;
58                     //取出要处理的部分位 
59                     nstatus|=((nclock+1) & 3) << (2*(M[i][j]));
60                     //填入处理后的部分位 
61                 }
62             if(!vis[nstatus])
63             {
64                 Q.push(Node(nstatus,i,len-1));
65                 vis[nstatus]=1;
66             }    
67         }    
68     }
69     len--;
70     stack<int> S;
71     while(1)
72     {
73         S.push(L[len].move+1);
74         len=L[len].pre;
75         if(L[len].pre<0)
76             break;
77     }
78     printf("%d",S.top());
79     S.pop();
80     while(!S.empty())
81     {
82         int t=S.top();
83         S.pop();
84         printf(" %d",t);
85     }
86     printf("
");
87 }
88 int main()
89 {
90     //freopen("in.txt","r",stdin);
91     //freopen("out.txt","w",stdout);
92     Init();
93     Solve();
94     return 0;
95 }

 eg.位运算举例

1.两位二进制数表示一个状态,4个一组,即8位二进制数表示一组状态

2.要实现的操作,对第i+1个状态进行加一操作

3.大体思路:1)待处理状态num做一个副本n; 2)num把要处理掉的那一位挖空 ;3)把待处理单位sub从n取出;

                 4)对sub操作 ;5)把sub塞回去

 1 void Init()
 2 {
 3     int num=0;
 4     for(int i=0;i<4;i++)
 5         num|=(Num[i]<<(i*2));
 6 }
 7 void Change(int i)
 8 {
 9     int n=num;
10     num&=((1<<8)-1)^(3<<(i*2));
11     //把要处理的那一位挖空为什么是3呢,因为二进制"11"为3,正好把两个二进制位"挖空"
12     int sub=(n>>(i*2))&3;//这样除了最低的两位其他的都会与0做&运算,都扑街了
13     sub+=1;
14     num|=(sub<<(2*i));
15 } 
原文地址:https://www.cnblogs.com/cnXuYang/p/6659520.html