USACO The clocks, ariprog

先八卦幾句。我們常說算法設計的目的是爲了達到獲得最優時空開銷,嗯,直接點看,是這樣的。不管現代計算機CPU有多快,內存可以大到哪裏去,資源總歸是有限的,物理學裏面叫做什麼熵熵熵的,俺這四年專門掛科,也記不清了。既然資源有限,那麼就一定得拼了老命滴想法子來在有限的資源上構建最佳的上層建築,所謂最佳,就是前述所言之:最優時空開銷。爲了達到這個目的,就我目前淺薄的知識範圍與領悟,一般有三種辦法:

l  從軟件工程的角度進行系統設計;

l  從數學的角度進行算法設計;

l  從計算機組成原理的角度進行優化處理。

每一項都是學問,精細說來非吾輩才力所能及也,莊子曰:飾小說以幹縣令,其於大達亦遠矣。USACOclocks可以從一個極細微處帶領我們感受感受上述的第三條。這個極細微處就是最基本的位運算。

先看代碼:

/*

ID: fairyroad

PROG: clocks

LANG: C++

*/

#include<fstream>

using namespace std;

 

//#define DEBUG

#ifdef DEBUG

    static int loops = 1;

    ofstream fo("debug.txt");

#endif

const int MAXNUM = 9;

const size_t MAXSEQ = 28; // every operator can be used up to 3 times, totally there're 9 kinds of operators

int min_len = MAXSEQ;

int res[MAXSEQ] = {-1}, path[MAXSEQ] = {-1}, aux[MAXNUM];

 

const int deals[MAXNUM] = {18911232, 19136512, 2363904, 16810048, 2134536, 262657, 36936, 73, 4617};

 

const int CLR = 57521883;

/*

inline void rotate(int& i) { i = i % 12 + 3;}

inline void derotate(int& i){ i = (i-3 ? i-3:12);}

*/

inline void copy(int source[]){

    for(int i = 0; i < min_len; ++i)

        res[i] = source[i];

}

 

void dfs(int path[], int src, int aux[], int curr_len, int start){

       if(curr_len >= min_len) return;

       if(src == 0){ // Now the length of the new path must be shorter

              min_len = curr_len;

              copy(path);

              return;

       }

 

       for(int i = start; i < MAXNUM; ++i){

              if(aux[i] < 4){

                  int ori = src;

            src = (src + deals[i]) & CLR;

            path[curr_len++] = i;

            ++aux[i];

#ifdef DEBUG

            fo<<"Loop "<<loops++<<"/nSRC: ";

            fo<<src;

            fo<<"/nAUX: ";

            for(int j = 0; j < MAXNUM; ++j)

                fo<<aux[j]<<' ';

            fo<<"/nPATH: ";

            for(int j = 0; j < curr_len; ++j)

                fo<<path[j]<<' ';

            fo<<"/n/n";

#endif

                     dfs(path, src, aux, curr_len, i);

                     // rolling back

                     src = ori;

                     path[--curr_len] = -1;

                     --aux[i];

              }

       }

}

 

int main()

{

       ofstream fout("clocks.out");

       ifstream fin("clocks.in");

       //vector<int> path(0), aux(MAXNUM, 0);

       int num, src = 0;

       for(int i = 0; i < MAXNUM; ++i){

           fin>>num;

           src += ((num/3)%4)<<((8-i)*3);

       }

 

       dfs(path, src, aux, 0, 0);

       for(int i = 0; i < min_len-1; ++i)

              fout<<res[i]+1<<' ';

       fout<<res[min_len-1]+1<<endl;

       return 0;

}

從算法整體設計上看,典型的深度優先搜索題,我也就不多說了,對於搞CS的來說,不懂得貪心、DP、深搜/廣搜等基本算法設計技巧,就像學物理的不知道牛頓三定律,學數學的不知道微積分,學Java的不知道interface,學UNIX的不知道文件概念一樣,不可理解,更不可原諒啊!

說位運算問題。注意到時鐘只有四種狀態,12點,3點,6點,9點。令它們爲0123,這樣的定義好處很大,因爲它們對應的二進制數爲:000001010011。即,我們用三個位來記錄一個時鐘的狀態,爲什麼不用兩位呢?考慮一下00+1=01, 01+1=10, 10+1=11, 11+1=? 要改變一個時鐘的時候,就給該位加上一,再用按位與的方法去除高位的1,與數字57521883(考慮一下這位大叔的二進制表示)按位與運算可用於清除每個時鐘狀態最高位的1,進一步地,可以用下面的數組來表示題目中的9種旋轉操作:

const int deals[MAXNUM] = {18911232, 19136512, 2363904, 16810048, 2134536, 262657, 36936, 73, 4617}; // How to generate this array? You know it~

9個時鐘都回歸12點的時候,恰好是src爲全0的狀態。這樣,判斷每次操作後src是否爲全0,就知道是否求出可行解。

再折回之前的話題,說到第三點:從計算機組成原理的角度進行優化處理。計算機組成原理裏應該有說的吧,位運算是計算機最基本的操作,各種位運算的組合纔有了數學世界裏的神馬神馬加減乘除開方次方blah blah blah。從空間使用角度看位運算也是最大限度使用了空間資源,其實一般的bool,完全可以由一個bit位來完成不是,不過爲了抽象與其他的考慮(比如內存對齊等等),一般還是8 bits。在海量數據面前,也許就還得迴歸1 bit的世界了,當然,您要說分佈式的並行處理,也中,那是另外的話題。

Arithmetic Progressions是道水題,直接上代碼,窮搜+基本剪枝。

/*

ID:fairyroad

LANG:C++

TASK:ariprog

*/

 

#include<fstream>

#include<algorithm>

using namespace std;

 

struct point{

       int a;

       int b;

};

point pt[20000];

 

inline bool myComp(point p1, point p2) {

    return (p1.b<p2.b)||((p1.b==p2.b)&&(p1.a<p2.a));

}

 

int main(){

      ifstream fin("ariprog.in");

      ofstream fout("ariprog.out");

 

       bool flag[125001];

       int bisquare[32000];

       int n, m, len = 0, count = 0;

 

       fin>>n>>m;

       for (int i=0; i<=125000; i++) flag[i]=false;

       for (int i=0; i<=m; i++)

         for (int j=0; j<=m; j++)

            flag[i*i+j*j]=true;

 

       m=m*m*2;

       for (int i=0;i<=m;i++)

        if (flag[i]){

            len++;

            bisquare[len]=i;

        }

 

       for (int i=1; i<= len; i++) {

        for (int j=i+1; j <= len; j++) {

            if ((n-1)*(bisquare[j]-bisquare[i]) + bisquare[i] > m) break; // pruning

            bool tag=true;

            for (int k=1;k<=n-2;k++){

                if (!flag[bisquare[j]+(bisquare[j]-bisquare[i])*k]){

                    tag=false;

                    break;

                }

            }

            if (tag) {

                ++count;

                pt[count].a=bisquare[i]; // first item

                            pt[count].b=bisquare[j]-bisquare[i]; // tolerance

            }

        }

    }

 

       sort(pt, pt+count+1, myComp);

 

     if (count==0) fout<<"NONE"<<endl;

 

     for (int i=1; i<= count; i++)

        fout<<pt[i].a<<" "<<pt[i].b<<endl;

 

    return 0;

}

 

 

PS. 個人聯繫方式:

微博: http://t.sina.com.cn/g7tianyi

del.icio.us: http://delicious.com/fairyroad

豆瓣:http://www.douban.com/people/Jackierasy/

e-mail: [email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章