BottomCoder SRM 392 div2
points | result | time |
---|---|---|
250 | AC | 3m |
500 | AC | 17.5m |
1000 | AC | 25m |
500,1000がひどい問題だった。
250
2007年の各月にキャンディをいくつ食べたか(<=1000)が書いてある。キャンディはいつも月末に食べるとして、キャンディを食べるまでの、年始からの日数の平均を答えよ。(意訳)
やるだけですね。累積の取り方と最後のdoubleを忘れなければおっけー。最大でもintの範囲を全然出ないので、全部intで良い。
import java.util.Arrays; public class AverageCandyLifetime { public double getAverage(int[] eatenCandies) { int[] mo = {31,28,31,30,31,30,31,31,30,31,30,31}; long x = 0; int num = 0; for(int i = 0;i <= 11;i++){ if(i > 0)mo[i] += mo[i-1]; x += mo[i] * eatenCandies[i]; num += eatenCandies[i]; } return (double)x / num; } void tr(Object... o) { System.out.println(Arrays.deepToString(o)); } }
500
2つの文字列s1,s2(||<=50)が与えられる。各文字列にはそれぞれ1個の'*'が入っていて、これは0文字以上の文字列にマッチする。s1,s2を両方満たす最短の文字列を求めよ。存在しないときは"impossible"を返せ。
うわーめんどくせー問題〜というのが第一印象。
まず自分のやり方。s1,s2の*以前と以後を取り出す。ここから、共通prefixと共通suffixがわかるので、これらが一致しない場合"impossible". そして共通prefix,suffixを除くと、{"*〜〜", "〜〜*"}, {"*", "〜〜*〜〜"}, {"*", "*"}の3パターン(逆もある)あるので、それぞれについて最短文字列を計算する。
最初のパターンでは、*〜〜の〜〜の頭と〜〜*の〜〜のケツが最も一致しているものが最短となるので、それをforでまわして調べる。
2つ目のパターンは、*の両翼をただつなげればよい。3つ目は空文字列。
こうしてできた文字列と共通prefix,suffixをつなげればできあがり。
次にこの問題を3分半で解いたPetrのやり方。*のところに?をi(0からはじめる)文字埋め込む。もう一方の文字列には、s1-s2+i(>=0でなければならない)個埋め込んで、二つの文字列の長さを等しくする。そのあと、結果文字列をstrとすると、先頭から1文字ずつ走査して、s1側が?ならs2側の文字をstrに追加、s2側が?ならs1側の文字をstrに追加、s1側、s2側の文字がどちらも?でなければ、一致していればその文字をstrに追加、そうでなければiをインクリメントしてcontinue、とする。そして、strが完成したらそれが答え。iをでっかいところまでまわしてもできなかったら"impossible"とする。
うおおすげぇと思ったけどこれ3分半じゃ絶対かけねぇ!
import java.util.Arrays; public class TwoStringMasks { public String shortestCommon(String s1, String s2) { int i1 = s1.indexOf('*'); int i2 = s2.indexOf('*'); String pre1 = s1.substring(0, i1); String suf1 = s1.substring(i1+1); String pre2 = s2.substring(0, i2); String suf2 = s2.substring(i2+1); int cp = Math.min(pre1.length(), pre2.length()); int cs = Math.min(suf1.length(), suf2.length()); if(pre1.length() < pre2.length()){ if(!pre2.startsWith(pre1)){ return "impossible"; } }else{ if(!pre1.startsWith(pre2)){ return "impossible"; } } if(suf1.length() < suf2.length()){ if(!suf2.endsWith(suf1)){ return "impossible"; } }else{ if(!suf1.endsWith(suf2)){ return "impossible"; } } if(s1.length() == cp + cs + 1){ return pre2 + suf2; } if(s2.length() == cp + cs + 1){ return pre1 + suf1; } if(i1 > i2){ String f = s1.substring(cp, i1); for(int i = s2.length() - cs;i >= i2 + 1;i--){ String q = s2.substring(i2 + 1, i); if(f.endsWith(q)){ return pre2 + f + s2.substring(i); } } }else if(i1 < i2){ String f = s2.substring(cp, i2); for(int i = s1.length() - cs;i >= i1 + 1;i--){ String q = s1.substring(i1 + 1, i); if(f.endsWith(q)){ return pre1 + f + s1.substring(i); } } }else{ return pre1 + suf1; } return null; } void tr(Object... o) { System.out.println(Arrays.deepToString(o)); } }
1000
n*n(<=10*10)のマスがある。これに0以上d-1(d<=n)以下の整数を埋めこんで、どの行・列も1回以上0〜d-1が現れて、かつそのようなもののうち1行目から文字列化したときに辞書順で最小のものを求めよ。
サンプルが親切じゃなかったら絶対わからなかった問題。試行して答えを出す系の問題だと思う。
先頭からn-d行とn-d列は一意に決まり、0 0 0 1 2 3 … d-1 という風になる。残ったd行d列に、各行各列、0〜d-1が1回ずつ現れるようにすれば良い。
残った各行はd個の順列で表せる。そしてnextPermutationで回す順に辞書順となっている。nextPermutationで回して、すでに埋まった行と数字がかぶってなければそれを採用。というふうにしてn-d+1行目から埋めていけば良い。
これで正解だったけれど、なぜこれで正解が出るのか謎。少なくともコンテスト中では証明できないよなぁ。
import java.util.Arrays; public class QuasiLatinSquares { public String[] makeSquare(int n, int d) { int[][] b = new int[n][n]; int u = n - d; for(int i = 0;i < u;i++){ for(int j = 0;j < n;j++){ b[i][j] = b[j][i] = Math.max(0, j - u); } } int[] used = new int[d]; for(int i = u;i < n;i++){ int[] ord = new int[d]; for(int j = 0;j < d;j++)ord[j] = j; outer: do{ for(int j = 0;j < d;j++){ if((used[j]&1<<ord[j])!=0){ continue outer; } } for(int j = 0;j < d;j++){ used[j]|=1<<ord[j]; b[i][j+u] = ord[j]; } break; }while(nextPermutation(ord)); } String[] ret = new String[n]; for(int i = 0;i < n;i++){ StringBuilder sb = new StringBuilder(); for(int j = 0;j < n;j++){ sb.append(b[i][j]); if(j < n - 1)sb.append(" "); } ret[i] = sb.toString(); } return ret; } public static boolean nextPermutation(int[] src) { int i; for(i = src.length - 2;i >= 0 && src[i] > src[i+1];i--); if(i == -1)return false; int j; for(j = i + 1;j < src.length && src[i] < src[j];j++); int d = src[i]; src[i] = src[j - 1]; src[j - 1] = d; for(int p = i + 1, q = src.length - 1;p < q;p++,q--){ d = src[p]; src[p] = src[q]; src[q] = d; } return true; } void tr(Object... o) { System.out.println(Arrays.deepToString(o)); } }