先八卦几句。我们常说算法设计的目的是为了达到获得最优时空开销,嗯,直接点看,是这样的。不管现代计算机CPU有多快,内存可以大到哪里去,资源总归是有限的,物理学里面叫做什么熵熵熵的,俺这四年专门挂科,也记不清了。既然资源有限,那么就一定得拼了老命滴想法子来在有限的资源上构建最佳的上层建筑,所谓最佳,就是前述所言之:最优时空开销。为了达到这个目的,就我目前浅薄的知识范围与领悟,一般有三种办法:
l 从软件工程的角度进行系统设计; |
l 从数学的角度进行算法设计; |
l 从计算机组成原理的角度进行优化处理。 |
每一项都是学问,精细说来非吾辈才力所能及也,庄子曰:饰小说以干县令,其于大达亦远矣。USACO的clocks可以从一个极细微处带领我们感受感受上述的第三条。这个极细微处就是最基本的位运算。
先看代码:
/*
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点。令它们为0,1,2,3,这样的定义好处很大,因为它们对应的二进制数为:000,001,010,011。即,我们用三个位来记录一个时钟的状态,为什么不用两位呢?考虑一下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: jacklinshi1004@gmail.com