USACO 1.4 clocks 分析

    技术2024-10-18  55

    Description就不啰嗦了,见USACO的题目地址吧

    http://ace.delos.com/usacoprob2?a=GYKcgnGbqEV&S=clocks

    Analysis

    由于同一种方法转4次相当于不转,所以转的方案数共有4^9种;

    9个钟的状态也共有4^9种;

    分别用1230来表示36912则最终状态为全0

     

    枚举:分别枚举9种方案的使用次数,然后判断是否可行,下面的这个程序给我了很多启发

    /* ID: MasterRay LANG: C++ TASK: clocks Executing... Test 1: TEST OK [0.022 secs, 2884 KB] Test 2: TEST OK [0.022 secs, 2884 KB] Test 3: TEST OK [0.022 secs, 2884 KB] Test 4: TEST OK [0.022 secs, 2884 KB] Test 5: TEST OK [0.011 secs, 2884 KB] Test 6: TEST OK [0.011 secs, 2884 KB] Test 7: TEST OK [0.022 secs, 2884 KB] Test 8: TEST OK [0.022 secs, 2884 KB] Test 9: TEST OK [0.022 secs, 2884 KB] */ #include <algorithm> #include <climits> #include <cstdio> #include <numeric> using namespace std; #define LOOP(x) for (s[x] = 0; s[x] < 4; ++s[x]) //很少用啊,强大 int main() { int a[9], b[9], cnt, min_cnt = INT_MAX, res[10], s[10]; freopen("clocks.in", "r", stdin); freopen("clocks.out", "w", stdout); for (int i = 0; i < 9; ++i) scanf("%d", &a[i]), a[i] = a[i]/3%4;//使用了逗号表达式适当精简代码 LOOP(1) LOOP(2) LOOP(3) LOOP(4) LOOP(5) LOOP(6) LOOP(7) LOOP(8) LOOP(9) { b[0] = (a[0]+s[1]+s[2]+s[4])%4; b[1] = (a[1]+s[1]+s[2]+s[3]+s[5])%4; b[2] = (a[2]+s[2]+s[3]+s[6])%4; b[3] = (a[3]+s[1]+s[4]+s[5]+s[7])%4; b[4] = (a[4]+s[1]+s[3]+s[5]+s[7]+s[9])%4; b[5] = (a[5]+s[3]+s[5]+s[6]+s[9])%4; b[6] = (a[6]+s[4]+s[7]+s[8])%4; b[7] = (a[7]+s[5]+s[7]+s[8]+s[9])%4; b[8] = (a[8]+s[6]+s[8]+s[9])%4; if (accumulate(b, b+9, 0)) continue;//用了一个小函数 cnt = accumulate(s+1, s+10, 0); if (cnt < min_cnt) { min_cnt = cnt; copy(s+1, s+10, res+1); } } for (int i = 1; i < 10; ++i) while (res[i]--) printf("%d%c", i, --cnt ? ' ' : '/n');//用了3目运算符 }

     

     

    Breadth First Search

    这是我做的方法,一开始没有用位运算来加速,所有就用了一个结构来保存,第一次用了结构内声明函数来计算hash值;

    /* ID: zhangji42 PROG: clocks LANG: C++ 不用非递减拓展 Executing... Test 1: TEST OK [0.032 secs, 15432 KB] Test 2: TEST OK [0.032 secs, 15432 KB] Test 3: TEST OK [0.076 secs, 15432 KB] Test 4: TEST OK [0.086 secs, 15432 KB] Test 5: TEST OK [0.108 secs, 15432 KB] Test 6: TEST OK [0.302 secs, 15432 KB] Test 7: TEST OK [0.378 secs, 15432 KB] Test 8: TEST OK [0.378 secs, 15432 KB] Test 9: TEST OK [0.378 secs, 15432 KB] 使用非递减拓展 Executing... Test 1: TEST OK [0.043 secs, 15432 KB] Test 2: TEST OK [0.043 secs, 15432 KB] Test 3: TEST OK [0.054 secs, 15432 KB] Test 4: TEST OK [0.032 secs, 15432 KB] Test 5: TEST OK [0.054 secs, 15432 KB] Test 6: TEST OK [0.097 secs, 15432 KB] Test 7: TEST OK [0.108 secs, 15432 KB] Test 8: TEST OK [0.097 secs, 15432 KB] Test 9: TEST OK [0.086 secs, 15432 KB] ABCDEFGHI 012345678 */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <time.h> const int maxlen = 262144+3; struct INFO { short c[9], type; int hash, pre; int gethash(){ //内置函数,面向对象的手法,不过这里用的挺好,不显累赘 hash = 0; for (int i = 0; i < 9; i++) hash = hash*4+c[i]; return hash; } INFO(){ pre = 1 << 20; hash = maxlen-1; type = 1;} //函数初始化 }; INFO q[maxlen]; bool h[maxlen]; INFO move(INFO q, int type, int pre) { q.pre = pre; q.type = type; char ch[7] = ""; switch (type) { case 1:strcpy(ch, "ABDE");break; case 2:strcpy(ch,"ABC");break; case 3:strcpy(ch,"BCEF");break; case 4:strcpy(ch,"ADG");break; case 5:strcpy(ch,"BDEFH");break; case 6:strcpy(ch,"CFI");break; case 7:strcpy(ch,"DEGH");break; case 8:strcpy(ch,"GHI");break; case 9:strcpy(ch,"EFHI");break; } for (int i = 0; i < strlen(ch); i++) q.c[ch[i]-'A'] = (q.c[ch[i]-'A']+1)%4; q.gethash(); return q; } void print(int now) { if (q[now].pre == 0) { printf("%d", q[now].type); return; } print(q[now].pre); printf(" %d", q[now].type); } /*for test:print the clock void printtest(INFO q) { for (int i = 0; i < 9; i++) { if (q.c[i] == 0) printf("12 "); else printf("%d ", q.c[i]*3); if (i % 3 == 2) printf("/n"); } printf("/n"); } */ int main(){ freopen("clocks.in", "r", stdin); freopen("clocks.out", "w", stdout); for (int i = 0; i < 9; i++) scanf("%d", &q[0].c[i]),q[0].c[i] = q[0].c[i]/3%4; memset(h,0,sizeof(h)); h[q[0].gethash()] = 1; /*Breath First Search*/ int now = 0, full = 0; INFO newget; if (!h[0]) while (now <= full) { for (int i = q[now].type; i <= 9; i++) { //使用了非递减拓展,大大优化了效率 newget = move(q[now], i, now); //printtest(newget);//to test whether 9 move is correct if (!h[newget.hash]) { q[++full] = newget; h[newget.hash] = 1; } if (newget.hash == 0) break; } //break;//test if (!newget.hash) break; now++; } /*print the path*/ if (full > 0) { print(full); printf("/n"); } //printf("Time used:%.4lf/n", (double)clock() / CLOCKS_PER_SEC); return 0; }   

    位运算加速BFS

    后来看了NOCOW上的位运算的思路,“

    这样,它们对应的二进制数为:000001010011。即,我们用三个位来记录一个时钟的状态(为什么不用两位?请思考)。

     

    tick一个时钟的时候,就给该位加上一,再用按位与的方法去除高位的1

     

    令最高的三位为时钟A,最低的三位为时钟I,那么:

     

    数“57521883”用于清除每个时钟状态最高位的一(按位与)。

     

    const long move[9] = {18911232, 19136512, 2363904, 16810048, 2134536, 262657, 36936, 73, 4617}

     

    move[i]表示题述中的第i+1种方法

     

    f[q]为原状态,比如用题述中的第k种方法,那么可以写成 f[q + 1] = (f[q] + move[k - 1]) & 57521883;

     

    9个时钟都回归12点的时候,巧的是状态f=0。这样,判断每个状态f是否为0,就知道是否求出可行解。

    上述方法要想用C++的程序过是不太可行的(除非你用要麻烦一下,把一个数拆成9个去hash,但这样就体现不出位运算的优势了),因为要开一个57521883大的BOOL数组,但在C++中空间分配至少是1个字节,BOOL的前7位是无用的0,所以大小达到了57521883/2^20 M = 54 M,如果能用1位来存贮,则使用54 / 8 M = 6.75 M,由于USACO评测系统给的内存只有16 M,所以就不行了,但用自己电脑测试还是可以的,速度比前一种方法快10%左右。

    还要特别注意的是:<<+的优先级,比如 1 << 2 + 3 = ?,不是7,而是32,因为 << 的优先级比 +/-

    /* ID: zhangji42 PROG: clocks LANG: C++ */ #include <stdio.h> #include <string.h> #include <time.h> const int move[9] = {18911232, 19136512, 2363904, 16810048, 2134536, 262657, 36936, 73, 4617}, maxlen = 262144+3; struct INFO { short type; int hash, pre; INFO(){ pre = 0; hash = 0; type = 0;} }; INFO q[maxlen]; bool h[57521883+1];//6.75M void print(int now) { if (q[now].pre == 0) { printf("%d", q[now].type); return; } print(q[now].pre); printf(" %d", q[now].type); } int main() { freopen("clocks.in", "r", stdin); freopen("clocks.out", "w", stdout); int clo; for (int i = 0; i < 9; i++) { scanf("%d", &clo); q[0].hash = q[0].hash << 3; q[0].hash += (clo / 3) % 4; } q[0].type = 1; memset(h,0,sizeof(h)); h[q[0].hash] = 1; int now = 0, full = 0, newhash; if (!h[0]) while (now <= full) { for (int i = q[now].type; i <= 9; i++) { newhash = (q[now].hash + move[i-1]) & 57521883; if (!h[newhash]) { q[++full].hash = newhash; q[full].pre = now; q[full].type = i; h[newhash] = 1; } if (!newhash) break; } if (!newhash) break; now++; } //printf("%d/n", now); if (full) { print(full); printf("/n"); } //printf("Time used:%.4lf/n", (double)clock() / CLOCKS_PER_SEC); return 0; }

    还有一种很强的数学方法,和解方程法;

    最新回复(0)