SuperCon2012予選結果
予選通過しました。
西日本勢なので大阪大学に行きます。
↓送ったコード
/* SuperCon 2012 予選問題C用テンプレート(問C,スーパーコン予選問題 兼 1級認定問題 2012版) ・解答プログラムはこのテンプレートに従って作成すること. ・解答プログラムは1つのファイルで,チーム名.c という名前にすること. ・入力の方式は,あらかじめ入力ファイル(例:input_sample.txt)を作っ ておき,実行時にファイル名を指定する方式です. */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <time.h> /* ↓以下の範囲は変更可能 */ #include <string.h> #define INF 0x7F7F7F7F int max(int a, int b){ return (a > b)?a:b; } int memo[1000][10001]; void init(int N){ int i, j; for(i = 0; i < N; i++){ for(j = 0; j < 10 * N + 1; j++){ memo[i][j] = INF; } } } int solve(int p, int l, int N, int v[1000], char w[1000][11], char W[10001]){ int i, t, ans; int k; /* strncmpの結果 */ int len = strlen(&w[p][0]); if(p == N){ return 0; } if(memo[p][l] != INF){ return memo[p][l]; } k = strncmp(&w[p][0], W + l, len); ans = solve(p + 1, l, N, v, w, W); if(k == 0){ ans = max(ans, solve(p + 1, l + len, N, v, w, W) + v[p]); }else if(k < 0){ t = v[p]; for(i = p + 1; i < N; i++){ if(v[i] > 0){ t += v[i]; } } ans = max(ans, t); } memo[p][l] = ans; return ans; } /* ↑上記の範囲は変更可能 */ int main(int argc, char** argv){ int answer = -1; /* この変数に答(Vの最大値)を代入してください */ int N; static int v[1000]; static char w[1000][10+1]; static char W[10000+1]; char* problem_file; clock_t start, end; FILE* fp; int i; char buf[0xffff]; char* p; if(argc <= 1){ fprintf(stderr, "Enter the input file.\n"); exit(EXIT_FAILURE); } problem_file = argv[1]; fp = fopen(problem_file, "r"); if(fp == NULL){ fprintf(stderr, "Cannot open %s.\n", problem_file); exit(EXIT_FAILURE); } p = fgets(buf, 0xffff, fp); assert(p != 0); N = atoi(strtok(buf, " \n")); assert(1 <= N && N <= 1000); p = fgets(buf, 0xffff, fp); assert(p != 0); p = strtok(buf, " \n"); strcpy(W, p); assert(1 <= strlen(W) && strlen(W) <= 10000); for(p = W; *p; ++p){ assert(islower(*p)); } for(i = 0; i < N; ++i){ p = fgets(buf, 0xffff, fp); assert(p != 0); v[i] = atoi(strtok(buf, " ")); assert(-1000000 <= v[i] && v[i] <= 1000000); p = strtok(NULL, " \n"); strcpy(w[i], p); assert(1 <= strlen(w[i]) && strlen(w[i]) <= 10); for(p = w[i]; *p; ++p){ assert(islower(*p)); } } fclose(fp); start = clock(); /* ↓以下の範囲は変更可能 */ init(N); answer = solve(0, 0, N, v, w, W); /* ↑上記の範囲は変更可能 */ end = clock(); printf("%s, %f, %d\n", problem_file, (double)(end - start) / CLOCKS_PER_SEC, answer); printf("%s, %d\n", problem_file, answer); return 0; } /* ↓以下の範囲は変更可能 */ /* ↑上記の範囲は変更可能 */
↓アルゴリズム説明(日本語が少し変)
アルゴリズム説明 文字列A = w_{S_0} + w_{S_1} +...+ w_{S_i} (S_i < N) 文字列B = w_(S_i + 1) とする. Aが,Wの先頭から|A|文字目までの文字列と等しいとき AにBを加えようとするとき (1)A+B <= W で, A+B は (Wの 先頭から|A+B|文字目までの文字列) と等しい (2)A+B < W で, A+B は (Wの 先頭から|A+B|文字目までの文字列) とは異なる (3)A+B > W である の3通りのどれかになる (1)のとき 認定問題のB問題と同じように考えることができる. BをAに加えた場合と,加えなかった場合で分けて,再帰的にといていく. (2)のとき A+Bにどんな文字列Cを加えても A+B+C < W となる. よって,文字列Cを,文字列B以降で正の価値を持つ文字列をすべて加えたもの とすることによって価値を最大化できる (3)のとき 文字列を加えることはできない 以上の解法を再帰関数を利用して実装した. また無駄な計算を減らすためにメモ化再帰の手法を用いた. メモ化の状態は [追加しようとしているwの番号][すでに作った文字列Aの長さ]とした. とりうる状態の数はたかだか(1000*10000)=10000000しかないので十分はやく計算できる.