上周末,我分别参加了CODE JAM的资格赛和北大的PKU比赛,正规的ACM比赛都是我第一次参加,中间也经历了很多的问题和磨难,打算分几次来写写自己的心得吧。
CODE JAM比赛是在线的比赛,必须提交测试数据集的运行结果和源代码。测试数据集分两种,一种small数据集,一种是large数据集。
第一题:http://code.google.com/codejam/contest/dashboard?c=975485#
题目大意是,A、B两个机器人在长廊中协作完成一个指定系列顺序的任务,机器人每移动一格需要一秒,按下一个按钮也需要一秒,必须严格按照指定系列完成任务,问最短完成的时间是多少?
还是比较简单的吧,可以声明两个数组atime 和 btime 分别代表A、B完成下一个任务时的时间,则可以得到这样一个结论,atime[afinal] = max( atime[alast] + amove , btime[bfinal]) + 1;代表意思是机器人A完成最新任务的时间为机器人A上一次的时间加上移动到当前任务位置的时间,或者是A移动到当前任务位置以后等待B完成最新任务以后的时间,加上执行任务的时间。同样道理btime[bfinal] = max(btime[blast] + bmove, atime[afinal]) +1;
代码如下:
#include <iostream> #include <fstream> #include <math.h> #define max(a,b) (a)>(b)?(a):(b) using namespace std; const int SIZE =101; ifstream fin("bot_large.in"); ofstream fout("bot_large.out"); int cas; int index =1; int step = 0; int b[SIZE] = {0}; int bl =0; int bp = 1; int o[SIZE] = {0}; int ol=0; int op = 1; void omove(int p) { int move = abs(p-op)+1+o[ol-1]; o[ol] = max( move, b[bl-1]+1); ol ++; op = p; } void bmove(int p) { int move = abs(p-bp)+1+b[bl-1]; b[bl] = max(move, o[ol-1]+1); bl ++; bp = p; } void read() { bp = 1; op = 1; bl = 1; ol = 1; b[0] =0; o[0] =0; fin >> step; char r; int p; for (int i =0; i < step; i ++) { fin >> r >> p; if (r == 'O') { omove(p); } else { bmove(p); } }//end for int result = max(o[ol-1], b[bl-1]) ; fout << "Case #" << index << ": " << result << endl; } int main() { fin >> cas; while (index <= cas) { read(); index ++; } return 0; }
第二题:http://code.google.com/codejam/contest/dashboard?c=975485#s=p1
题目大意是,一种游戏,有8个原字母,不断的添加字母,如果两个字母出现在组合逻辑中,则合并成新的字母,如果不能合并,则判断字母是否出现在排斥队列中,如果是的话,则消除整个队列(这个很重要,我第一次理解成了删除两个字母之间的所有字母)。
我的做法是建立两个二维数组,combine[i][j] = combine[j][i] = NEWCHA,表示i,j字母可以融合成新的字母,opp[i][j] = opp[j][i] = 1表示字母i到j互相排斥。则读取一个系列的时候,先判断合并,再判断是否可以消除。题目理解对了,就相对比较简单了,复杂的数据量也没有问题。
代码如下:
#include<iostream> #include<fstream> #include <string.h> using namespace std; const int SIZE = 26; const int N = 101; ifstream fin("mag_large.in"); ofstream fout("mag_large.out"); int cas, index = 1; int c, d, n; int str[101]; int length; int combine[SIZE][SIZE]; int oppose[SIZE][SIZE]; void cmbop() { int a, b; if (length>1) { a =str[length-2]; b = str[length-1]; if (combine[a][b] != 0) { str[length-2] = combine[a][b]; length --; return; }//end if } a = str[length-1]; for (int i=0; i <= length-2; i ++) { b = str[i]; if (oppose[a][b] == -1) { length = 0; break; } } } void read() { memset(combine, 0, sizeof(int) * SIZE * SIZE); memset(oppose, 0, sizeof(int) * SIZE * SIZE); fin >> c; char c1, c2, c3; for (int i=0; i < c; i ++) { fin >> c1 >> c2 >> c3; combine[c1 - 'A'][c2- 'A'] = c3 - 'A'; combine[c2-'A'][c1-'A'] = c3-'A'; } fin >> d; int i; for (i=0; i < d; i ++) { fin >> c1 >> c2; oppose[c1-'A'][c2-'A'] = -1; oppose[c2-'A'][c1-'A'] = -1; } length = 0; fin >> n; char cha; for (i=0; i< n; i ++) { fin >> cha; str[length] = cha - 'A'; length ++; cmbop(); } } void print() { fout<< "Case #" << index <<": ["; if (length>0) { char c = 'A' + str[0]; fout << c; } for (int i=1; i < length; i ++) { char c = 'A' + str[i]; fout << ", " << c; } fout << "]" << endl; } int main() { fin >> cas; while (index <= cas) { read(); print(); index ++; } return 0; }
第三题:http://code.google.com/codejam/contest/dashboard?c=975485#s=p2
题目大意是:两个儿子在分糖果,然后小儿子比较笨,只会用异或的办法求和,只要小儿子觉得两边的糖果相等,就不会哭。然后要求大儿子在不把小儿子弄哭的情况下,分糖果,并且保证自己的糖果序号和最大。
第一次看到这道题的时候,由于小数据量不大,2 ≤ N ≤ 15.因此我使用的方法是采用0-1遍历的方法,0代表不取,1代表取。遍历出所有的情况,最后进行判断。
代码如下:
#include<iostream> #include <fstream> using namespace std; ifstream fin("candy.in"); ofstream fout("candy.out"); const int SIZE = 1000; int cas, index=1; int n; int arr[SIZE]; int path[SIZE]; int csum; int cpsum; int psum; int pcsum; int result =0; void read() { fin >> n; for (int i=0; i < n; i ++) { fin >> arr[i]; } result =0; } void candy(int c) { if (c >= n) { csum = 0; psum = 0; cpsum =0; pcsum = 0; for (int i=0; i< n; i ++) { if (path[i] == 0) { csum += arr[i]; cpsum = cpsum^arr[i]; } else { pcsum += arr[i]; psum = psum ^ arr[i]; } } if (cpsum == psum&&pcsum!=0) { if (result < csum) { result = csum; } } return; } path[c] = 0; candy(c+1); path[c] =1; candy(c+1); } void print() { if (result == 0) { fout << "Case #" << index<<": NO"<<endl; } else fout <<"Case #"<<index<<": "<<result<<endl; } int main() { fin >> cas; while (index <= cas) { read(); candy(0); print(); index ++; } return 0; }
但是对于大数据量来说,复杂度为2^n,明显过不了。因此,但是分析一下,假设原系列为A1, A2,A3,……An。那么小儿子觉得相等的情况为Ai ^ Aj ^ Ak ^ …… = Ax ^ Ay ^ Az ^ ……。那么也就是说,如果糖果是可分的话,A1 ^ A2 ^ A3 ……= 0,那么两边同时异或最小的数 A1^A2 ^ A3^ ……^An = Amin,因此,如果整个异或为0,则只要所有数求和减去最小的数,就是结果了,代码如下:
#include<iostream> #include <fstream> using namespace std; ifstream fin("candy_large.in"); ofstream fout("candy_large.out"); const int SIZE = 1000; int cas, index=1; int n; int arr[SIZE]; int result =0; void read() { fin >> n; for (int i=0; i < n; i ++) { fin >> arr[i]; } result =0; } void candy() { int small = 0x7fffffff; int psum = 0; for (int i=0; i<n; i ++) { if (arr[i] < small) { small = arr[i]; } psum = psum ^ arr[i]; result += arr[i]; } if (psum == 0) { result -= small; } else result = 0; } void print() { if (result == 0) { fout << "Case #" << index<<": NO"<<endl; } else fout <<"Case #"<<index<<": "<<result<<endl; } int main() { fin >> cas; while (index <= cas) { read(); candy(); print(); index ++; } return 0; }
最后一题比较复杂,下回单独说吧。