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\n",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("\n");
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 }
时间: 2024-12-29 11:30:15

POJ 1166 The Clocks 位运算与BFS的相关文章

POJ 1166 The Clocks (爆搜 || 高斯消元)

题目链接 题意: 输入提供9个钟表的位置(钟表的位置只能是0点.3点.6点.9点,分别用0.1.2.3)表示.而题目又提供了9的步骤表示可以用来调正钟的位置,例如1 ABDE表示此步可以在第一.二.四.五个钟调正,如原来是0点,那么调正后为3点.问经过那些步骤可以导致9个钟的位置都在0点. 分析: 这个本来是一个高斯消元的题目,但是 听说周期4不是素数, 求解过程中不能进行取余.因为取余可能导致解集变大. 不过也有用高斯消元做的,下面是用高斯消元的分析 ” Discuss也有人讨论了,4不是质数

POJ 1166 The Clocks 高斯消元 + exgcd(纯属瞎搞)

根据题意可构造出方程组,方程组的每个方程格式均为:C1*x1 + C2*x2 + ...... + C9*x9 = sum + 4*ki; 高斯消元构造上三角矩阵,以最后一个一行为例: C*x9 = sum + 4*k,exgcd求出符合范围的x9,其他方程在代入已知的变量后格式亦如此. 第一发Gauss,蛮激动的. #include <algorithm> #include <iostream> #include <cstring> #include <cstd

Poj 1166 The Clocks(bfs)

题目链接:http://poj.org/problem?id=1166 思路分析:题目要求求出一个最短的操作序列来使所有的clock为0,所以使用bfs: <1>被搜索结点的父子关系的组织: 在bfs中,队列中存储着解答树中搜索过的结点,并且每个结点都可以使用它在队列中的位置作为其唯一的ID: 另外,使用另一个数组存储结点的父节点和从父节点到该节点的操作,同样的,使用结点在队列中的位置作为该节点的ID: 这种方法类似于并查集的方法,使用多个线性数组来模拟树的结构:这里模拟解答树中搜索过的结点构

poj 1166 The Clocks 记录路径的广搜

题意: 给9个时钟的初始状态,和一些对某几个钟的操作,求最少经过几步能到目标状态(全指向12点). 分析: 明显的广搜,但实现起来的细节要注意:1.因为要记录路径,所以要在整个程序执行过程中扩展出的节点在输出路径前不能销毁, 故采用静态内存分配的方法(开node[600000],用get_node()创建节点.2.queue<node>比queue<int>要多花1别的时间. //poj 1166 //sep9 #include <iostream> #include

POJ 2436 二进制枚举+位运算

题意:给出n头牛的得病的种类情况,一共有m种病,要求找出最多有K种病的牛的数目: 思路:二进制枚举(得病处为1,否则为0,比如得了2 1两种病,代号就是011(十进制就是3)),首先枚举出1的个数等于k的二进制数,然后跟所有的牛的代号一一比较,符合的           +1,找出其中和最大的:就是转换2进制麻烦,用位运算就好实现了,但是位运算不是很明白含义,明白了再补充: 知识点: 3 & 2 = 2,相同为1,不同为0, 011 & 010 = 010:(怎么利用的这个特点不明白):

POJ 2777 Count Color(位运算+线段树+lazy+区间更新)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 39905   Accepted: 12034 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

poj 1166 The Clocks (暴搜)

The Clocks Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 15944   Accepted: 6493 Description |-------| |-------| |-------| | | | | | | | |---O | |---O | | O | | | | | | | |-------| |-------| |-------| A B C |-------| |-------| |-------|

poj 3225 线段树+位运算

略复杂的一道题,首先要处理开闭区间问题,扩大两倍即可,注意输入最后要\n,初始化不能随便memset 采用线段树,对线段区间进行0,1标记表示该区间是否包含在s内U T S ← S ∪ T 即将[l,r]标记为1I T S ← S ∩ T 即将-oo~l和r~+oo标记为0,因为是并集,所以并集后的集合s一定在[l,r]内,则在l,r内的集合被标记是什么状态就是什么状态(表示是否属于s),[l,r]外的集合不属于s所以标记为0D T S ← S - T  即将[l,r]标记为0,则在[l,r]内

poj 3071 概率DP 位运算

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Practice POJ 3071 Description Consider a single-elimination football tournament involving 2n teams, denoted 1, 2, …, 2n. In each round of the tournament, all tea