TopCoder SRM296 DIV1 Report
match date:Monday, April 3, 2006
report date:Monday, April 10, 2006
Preface:
該好好反省了,在DIV1始終還是不能有好的表現。這次靠幾個資料Challenge了幾個代碼,但題目還是沒能做出來,看來水平還是不濟啊。
Problem 250 - NewAlbum :
貌似簡單的一道題,可以用動規快速解決,但我當時只是覺得簡單,採用了直接算的方法,沒想到這題骯髒的資料這麼多。
從i=1開始處理到nSongs張歌曲,每個i的最佳擺法是:
best[i] = min( best[i] , best[i-j]+1 )j=1..min(i,cdCapacity)
其中j表示在best[i-j]張CD後,把j放在另一張CD內。當然必須保證j不能被13整除。
直接算也是有人過的,不過要考慮一些特殊的情況。包括如果剩下13張等待處理,而之前的CD都擺了15張時,可以從前面移一張過來,而如果前面的CD是擺放了14張的話則不行。
Problem 500 - StringReplacements:
第二題目用到了兩次遞規,看過解題報告後才知道,原來遞迴的思想是如此神奇,真該培養一下遞迴的能力了。
首先,先考慮如何計算由單個字母變化n次後的各字母數量。用一個DP數組是可以解決的。下式的mem是DP數組,t表示第t次變化,c表示從c字母開始變,i表示變成i字母。而move[i][j] 表示 i字母轉變一次後第j位的字母。
mem[t][c][i] = sum ( mem[t-1][ move[c][k] ][i] ) k=1..3
然後考慮如何取從left到right的位置之間的字母個數,由於任意字母轉變n次後的字母個數是3^n,所以只要計算3^n與left和right的關係就可以了,同時要注意3^n可能會超出整數範圍:
long[] solve( int c, int t, long left, long right) {// 遞迴求left與right之間的解
long[] res = new long[3];// 存放解
long max = (long) Math.pow(3, t);// 3^t 指最大範圍
if (left == right && left == 0 && t == 0) {// 特別處理一下只有一個字母
res[c]++;// 且不需要變換的情況
} else if (left <= 0 && right >= max) {// left和right的範圍很大
return getGen(c, t);// 可以直接返回前面求的men
} else if (left <= right) {
int del = (int) Math.pow(3, t – 1);// 求3^(t-1),指轉變後三個之// 間的間隔
for (int i = 0; i < 3; i++) {// 迴圈求三個子字母的解
left = left < 0 ? 0 : left;// left保證大於等於0
right = right > max ? max : right;// right保證小於等於最大可能
long[] add = solve(move[c][i], t - 1, left, right);// 遞迴求一個字母解
for (int j = 0; j < 3; j++) {
res[j] += add[j];// 累加解
}
left -= del;//left 變為下一個字母的left
right -= del;//變為下一個字母的right
}
}
return res;
}
Problem 1000 - ColoredBricks :
這題比較煩瑣,考驗編程速度和量的。首先求出所有的翻轉,共計24種,一個面可以有六個位置的可能性,這個面相鄰的面可以看出,通過一個旋轉,是有四種可能。因為只要固定了一個面和與它相鄰的那個面色子就固定了,所以6個可能乘4個可能就是24種情況。
// 301245 是從正面向上面旋轉
// 052413 是從左邊向上面旋轉
// 514302 是從左邊向正面旋轉
int[][] rotate = { { 3, 0, 1, 2, 4, 5 }, { 0, 5, 2, 4, 1, 3 },
{ 5, 1, 4, 3, 0, 2 } };
int[] rot(int[] x, int[] p) ;//旋轉函數,p是rotate中一個值,返回x轉後值
void generateRotate() {//產生24種可能
int i, j, k, l = 0;
int[] x = new int[] { 0, 1, 2, 3, 4, 5 };
r = new int[24][6];
for (i = 0; i < 4; i++) {
x = rot(x, rotate[0]);
for (j = 0; j < 4; j++) {
x = rot(x, rotate[1]);
NEXT: for (k = 0; k < 4; k++) {
x = rot(x, rotate[2]);
for (int[] e : r) {
if (Arrays.equals(e, x))
continue NEXT;
}
r[l++] = x.clone();
}
}
}
}
下一個要考慮的問題是著色問題,一樣用暴力的方法來解決。用7層for語句對一個brick枚舉所有的著色,然後計算該著色對於所有輸入的顏色差,取一個最小值就是結果了。計算一種著色與一個輸入的顏色差需要考慮到24種旋轉。
Links:
My Blog:
http://blog.csdn.net/ray58750034/
My statistic:
http://www.topcoder.com/stat?c=coder_room_stats&rd=9817&cr=20862220
SRM 289 - Problem Set & Analysis:
http://www.topcoder.com/tc?module=Static&d1=match_editorials&d2=srm296