http://ace.delos.com/usacoprob2?S=clocks&a=3hMw0XdJ4KO
枚举暴力搜索法
虽然为暴利搜索,不过结合题目的特点还是需要注意一些技巧的,注意该题目的特点:
1每种变换方法可以使用0~3次,因为如果使用4的话,正好转一圈,和0等效。
2并且每种变换方法在前与在后是等效的。所以根据题目输出要求,最小字典序。
该法枚举每条路径,枚举的顺序很重要,因为找到最小的就停止搜索了,所以控制好枚举顺序有些技巧。
根据排列组合基础可以知道,该问题的每种path路径为 1,2,3,4,5,6,7,8,9,11,12,13,。。。。998,999,1112,1113,1114.。。。
注意,999之后按照正常字典序的话应该是1111,但是正确的应该是1112,这个很关键,否则会出现组合爆炸情形的,因为如果直接按照字典序搜索的话,需要的总搜索次数为:
因为每种数字在path路径中最多出现3次,所以path的最长长度为27,一共就有9^27种,这个数据量是肯定会超时的!!!
但是如果加一个相同数字不能超过3次的限制之后,总的搜索次数就会大大下降,一共是4^9 = 262144 个,少了好多,可以暴力搜索了。。。
在这个“局限字典序”的生成上用的递归DFS。。。
/* ID:liuweiv2 PROG:clocks LANG:C++ */ #include<iostream> using namespace std; #include<fstream> int orig[3][3]; int temp[3][3]; char moves[10][6] = {"","ABDE","ABC","BCEF","ADG","BDEFH","CFI","DEGH","GHI","EFHI"}; int path[28]; int count[28]; ifstream infile; ofstream outfile; void copy(){ for(int i=0;i<3;i++) for(int j = 0;j<3;j++) temp[i][j] = orig[i][j]; } bool perChange(char c,bool isAdd){ if(c == '\0') return false; int p ; if(isAdd) p = 3; else p = -3; int t = c - 'A'; int i = t/3; int j = t%3; temp[i][j]+=p; if(temp[i][j] == 15) temp[i][j] = 3; if(temp[i][j] == 0) temp[i][j] = 12; return true; } void change(int moveNum,bool isAdd){ for(int i=0;i<6;i++) if(perChange(moves[moveNum][i],isAdd) == false) break; } void print(){ for(int i=0;i<3;i++){ for(int j=0;j<3;j++) cout<<temp[i][j]<<" "; cout<<endl; } cout<<endl; } bool check(){ for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(temp[i][j]!=12) return false; return true; } bool isFind = false; void dfs(int step,int max){ if(isFind) return; if(step>max){ //到达叶子节点,检查 copy(); for(int i=1;i<step;i++) change(path[i],true); if(check()){ isFind = true; outfile<<path[1]; for(int i=2;i<step;i++) outfile<<" "<<path[i]; outfile<<endl; } return ; } int i; if(step == 1) i = 1; else i = path[step-1]; for(;i<=9;i++){ if(isFind) return; path[step] = i; count[i]++; if(count[i] == 4){ count[i]--; continue; } dfs(step+1,max); count[i]--; } return; } int main(){ infile.open("clocks.in"); outfile.open("clocks.out"); //memset(count,0,sizeof(count)); for(int i=0;i<3;i++) for(int j = 0;j<3;j++) infile>>orig[i][j]; for(int i=1;i<=27;i++){ if(isFind) break; dfs(1,i); } return 0; }
Executing...
Test 1: TEST OK [0.000 secs, 3344 KB]
Test 2: TEST OK [0.000 secs, 3344 KB]
Test 3: TEST OK [0.022 secs, 3344 KB]
Test 4: TEST OK [0.032 secs, 3344 KB]
Test 5: TEST OK [0.022 secs, 3344 KB]
Test 6: TEST OK [0.119 secs, 3344 KB]
Test 7: TEST OK [0.119 secs, 3344 KB]
Test 8: TEST OK [0.162 secs, 3344 KB]
Test 9: TEST OK [0.119 secs, 3344 KB]
All tests OK.
由于在操作上没有进行优化,直接进行的最直接的模拟,所以耗时还是比较严重的!抽空用“位加速”修改一下 !!!