POJ1222 EXTENDED LIGHTS OUT(枚举方法)
题目地址:http://poj.org/problem?id=1222
最近写了许多的算法题,里面也有一些很有意思的题,但没有更新博客,今天起打算更新前一阶段学习算法的成果。
和以往不同的是,以后的代码思路都体现在详尽的注释里。
import java.util.Scanner;
public class Main{
/*解题思路
*
* 1.时钟的状态只有四种(分别对应0.1.2.3),那么顺时针旋转90°
* 就是clock_time = (clock_time+1)%4,九个时钟就用一个
* 数组表示它们的状态。
* 2.同时每一种操作都有周期性限制,也即最多需要几次操作,多于这个
* 次数产生循环。本题中,操作的最多次数为3,如果操作四次,则和没有
* 操作是一样的..故用一个数组表示九个操作每个操作需要进行的次数。
* 3.对移动方法进行枚举,枚举方法123,共64种情况,则A的状态只
* 能通过操作4来改变,所以要A达到十二点,那么操作4的次数是一定的了。
* 同理,改变B只能操作5,改变C只能操作6。那么操作123456的次数确定。
* 4.这时候,同理改编D只能操作7,改编F只能操作9,所以12345679确定。
* 5.这时候还剩EGHI还没确定,操作8还没确定。由于操作8只能同时改编GHI,
* 不改变E,那么看看E是否已经到达十二点,看看GHI是否相同。
*
*/
static private int[] clock_time; //时钟初始状态
static private int[] enumeration; //用于枚举123操作,初始000
static private int min_time = 28; //记录当前最少序列
static private int[] min_seq;
//9种操作
static private int [][] op = new int[][]{
//A B C D E F G H I
{1,1,0,1,1,0,0,0,0}, //op1:ABDE
{1,1,1,0,0,0,0,0,0}, //op2:ABC
{0,1,1,0,1,1,0,0,0}, //op3:BCEF
{1,0,0,1,0,0,1,0,0}, //op4:ADG
{0,1,0,1,1,1,0,1,0}, //op5:BDEFH
{0,0,1,0,0,1,0,0,1}, //op6:CFI
{0,0,0,1,1,0,1,1,0}, //op7:DEGH
{0,0,0,0,0,0,1,1,1}, //op8:GHI
{0,0,0,0,1,1,0,1,1} //op9:EFHI
};
static private void input(){
Scanner sc = new Scanner(System.in);
clock_time = new int[9];
for(int i = 0; i < 9; i++){
clock_time[i] = sc.nextInt();
}
}
//枚举操作123的不同次数,每次枚举都+1
static private void enumerate(){
enumeration[0]++;
if(enumeration[0] == 4){
enumeration[1]++;
enumeration[0] = 0;
}
if(enumeration[1] == 4){
enumeration[2]++;
enumeration[1] = 0;
}
}
//由一种枚举状态,确定此状态能否达成要求,若达到要求,返回总的操作次数,达不到要求,返回28次
//1.确定123的操作次数(已经得到)
//2.施加123操作给所有时钟,得到ABC的状态,从而确定456的操作次数
//3.施加456操作给所有时钟,得到DF的状态,从而确定79的操作次数
//4.施加79操作给所有时钟,得到EGHI的状态
//5.判断GHI是否相同,E是否达到12点,若其一条件不满足,则返回28次
//6.如果满足,确定8操作的次数。输出所有操作,根据次数确定该操作重复输出的次数
public static void guess(int []enumeration){
// System.out.println("本次枚举:");
// for(int i = 0; i < 3; i++){
// System.out.print(enumeration[i] + " ");
// }
// System.out.println();
//临时记录状态的时钟
int [] t = new int[9];
for(int i = 0; i<9;i++){
t[i] = clock_time[i];
}
//每种操作执行的次数
int [] opn = new int[9];
opn[0] = enumeration[0];
opn[1] = enumeration[1];
opn[2] = enumeration[2];
//施加123操作给所有时钟
for(int i = 0; i < 3; i++){
for(int j = 0; j < 9; j++){
t[j] = (t[j] + opn[i] * op[i][j])%4;
}
}
// System.out.println("施加操作123后:");
// for(int j = 0; j < 9; j++){
// System.out.print(t[j] + " ");
// }
// System.out.println();
//通过ABC的状态,得到456的操作次数
opn[3] = (4 - t[0]) % 4;
opn[4] = (4 - t[1]) % 4;
opn[5] = (4 - t[2]) % 4;
//施加456操作给所有时钟
for(int i = 3; i < 6; i++){
for(int j = 0; j < 9; j++){
t[j] = (t[j] + opn[i] * op[i][j])%4;
}
}
// System.out.println("施加操作456后:");
// for(int j = 0; j < 9; j++){
// System.out.print(t[j] + " ");
// }
// System.out.println();
//通过DF,确定79的操作次数
opn[6] = (4 - t[3]) % 4;
opn[8] = (4 - t[5]) % 4;
//施加79操作给所有时钟
for(int i = 6; i <= 8; i++){
if(i == 7)
continue;
for(int j = 0; j < 9; j++){
t[j] = (t[j] + opn[i] * op[i][j])%4;
}
}
// System.out.println("施加操作79后:");
// for(int j = 0; j < 9; j++){
// System.out.print(t[j] + " ");
// }
// System.out.println();
//判断
if(t[4] != 0 || t[6] != t[7] || t[7] != t[8])
{
// System.out.println("本次不满足条件");
return;
}
//计算操作8
opn[7] = (4 - t[6]) % 4;
// System.out.println("本次操作序列:");
int sum = 0;
for(int i = 0; i < 9; i++){
sum += opn[i];
}
if(sum < min_time){
min_seq = opn;
}
else
return;
}
public static void main(String[] args) {
input();
enumeration = new int[3]; //自动初始化000
for(int i = 0; i < 64;i++){
guess(enumeration);
enumerate();
}
for(int i = 0; i < 9; i++){
for(int j = 0; j < min_seq[i]; j++){
System.out.print(i + 1 + " ");
}
}
}
}