您的位置:首页 > 运维架构

openjudge-拨钟问题

2017-01-30 00:13 351 查看

描述

有9个时钟,排成一个3*3的矩阵。

|——-| |——-| |——-|

| | | | | | |

|—O | |—O | | O |

| | | | | |

|——-| |——-| |——-|

A B C

|——-| |——-| |——-|

| | | | | |

| O | | O | | O |

| | | | | | | | |

|——-| |——-| |——-|

D E F

|——-| |——-| |——-|

| | | | | |

| O | | O—| | O |

| | | | | | | |

|——-| |——-| |——-|

G H I

(图 1)

现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。

移动 影响的时钟

1 ABDE

2 ABC

3 BCEF

4 ADG

5 BDEFH

6 CFI

7 DEGH

8 GHI

9 EFHI

输入

9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。

输出

输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。

样例输入

3 3 0

2 2 2

2 1 2

样例输出

4 5 8 9

来源

1166

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;

unsigned int clocks[10];
//保存最后最少的移动次数,最多也就是27次
int min_res = 28, tmp = 0;
//保存最小次数时候的移动方法
unsigned int min_op[10] = {0}, op[10] = {0};

void isOk(const unsigned int i1,const unsigned int i2,const unsigned int i3);//如果验证成功 那么返回1  否则返回0

void enumerate()//将前3行的拨钟数目进行枚举 返回枚举方法 循环写在这里面
{
for(unsigned int i=0;i<4;i++)
{
for(unsigned int j=0;j<4;j++)
{
for(unsigned int k=0;k<4;k++)
{
isOk(i,j,k);
}
}
}
}
/*

1        ABDE
2        ABC
3        BCEF
4        ADG
5        BDEFH
6        CFI
7        DEGH
8        GHI
9        EFHI
*/
void isOk(const unsigned  int i1,const unsigned int i2 ,const unsigned int i3)//如果验证成功 那么返回1  否则返回0
{
op[1]=i1;
op[2]=i2;
op[3]=i3;
op[4]=(4-(clocks[1]+op[1]+op[2])%4)%4;//确定A
op[5]=(4-(clocks[2]+op[1]+op[2]+op[3])%4)%4;//确定B
op[6]=(4-(clocks[3]+op[2]+op[3])%4)%4; //确定C
op[7]=(4-(clocks[4]+op[1]+op[4]+op[5])%4)%4;//确定D
op[8]=(4-(clocks[7]+op[4]+op[7])%4)%4;//确定G
op[9]=(4-(clocks[5]+op[1]+op[3]+op[5]+op[7])%4)%4;//确定E
//f  h  i
if((clocks[6]+op[3]+op[5]+op[6]+op[9])%4==0  && (clocks[8]+op[5]+op[7]+op[8]+op[9])%4==0
&& (clocks[9]+op[6]+op[8]+op[9])%4==0)//满足条件
{

tmp = 0;
for (int j = 1; j <= 9; j++)
tmp += op[j];
if (tmp < min_res)//找到了更优秀的解
{
min_res = tmp;
memcpy(min_op, op, sizeof(unsigned int)*10);
return ;
}
else//不符合要求 直接return继续寻找
{
return ;
}
}
return ;
}
int main()
{
int tag0=0;
for (int i = 1; i <= 9; i++)
cin >> clocks[i];
//非零项排序
enumerate();
vector<unsigned int> res;
for (int i = 1; i <= 9; i++)
{
if (min_op[i] != 0)
{
tag0=1;
res.push_back(i);
min_op[i]--;
i--;
}
}
sort(res.begin(), res.end());
if(tag0)
{
/**/
for (vector<unsigned int>::iterator it = res.begin(); it != res.end(); it++)
cout << *it << ' ';
}
else    cout << "0";
cout <<endl;
return 0;
}


解题思路

/*这一题实质类似熄灯问题和画家问题。其共通点在于:

操作对环境的改变是无序的,每个操作都会影响到周围的状态。

同时每一种操作都有周期性限制,也即最多需要几次操作,多于这个次数产生循环。

熄灯问题中,每个灯最多熄灯一次,因为灯只有两种状态,

并且循环。而这里,有4种循环的状态,因此每个移动操作顶多使用3次。

我们对移动方法1,2,3进行枚举,每种方法无非实施0-3次,也即一共4^3=64种情况。

这些情况之间并非没有关系。

例如,我们确定了1,2,3的情况数,那么得到一个灯A,B,C的状态,而只有移动4能够改变A,

移动5能够改变B,移动6能够改变C,那么移动4-6的次数也确定了。

同样,这时只有移动7能够改变D,移动9能够改变F,这时移动7和9的次数也确定了。

最后,时钟A,B,C,D,F都已经到达12点,E,G,H,I还没确定,只剩下移动8能够改变GHI,

所以只要检查E是否已经到达12点以及,GHI的时钟数是否相等就行了。

最后找到一个移动次数最小的情况。

这题也可以用暴力搜索,因为最多有4^9个组合,不会超时。

这题还可以列出一个方程组,九个未知数,通过高斯消元法来解方程组。*/

//思路引用于:http://www.cnblogs.com/CCBB/archive/2011/09/13/2175043.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: