BottomCoder SRM 382 div2
points | result | time |
---|---|---|
250 | AC | 5.5m |
500 | AC | 13.5m |
1000 | AC | 17.5m |
250
N(<=50)項の数列a(0<=*<=1000000)がある。このうち、a[i]ではじまり[j]で終わる連続したj-i+1項について、j-i+1>=K(<=|A|)であるもののうち、j-i+1項の平均が最大となるような(i,j)を求めよ。平均が等しくなる場合は、j-i+1がより大きいものを選べ。
平均を求めるときにdoubleになるので誤差処理が必要?あとは普通に全探索でOK.ちなみに1000000*50*50は2^31より大きいので、Javaで無理してintでやろうとすると死ぬ。
public class ContiguousSubsequences { public int[] findMaxAverage(int[] a, int K) { int n = a.length; int maxn = 0; double maxa = 0; int maxi = 0; double EPS = 1E-7; for(int i = 0;i < n;i++){ for(int j = i + K - 1;j < n;j++){ int sum = 0; for(int k = i;k <= j;k++){ sum += a[k]; } double ave = (double)sum / (j-i+1); if(ave > maxa + EPS || (Math.abs(ave - maxa) < EPS && maxn < j-i+1)){ maxa = ave; maxn = j-i+1; maxi = i; } } } return new int[]{maxi, maxi+maxn-1}; } }
500
N*M(<=10*10)のチェス盤の上にK-riderが何個か置いてある。K-riderは、1手でknightの動きをK回以下できる駒である。駒が同一マスにあっても問題ないとするとき、盤上のすべてのK-riderが一点に集まるために必要な手数を答えよ。そのようなことができない場合は-1を返せ。
N*Mが小さいので解き方に自由がきく。
1個のK-riderについて、全てのマスに最短何手でいけるかを計算する。knightの動きでM手でいけたとき、K-riderとしては[(M+K-1)/K]手でいける。MはBFSで列挙すればO((NM)^2)でできる。行けないところは-1をつけておく。
そして、全てのK-riderについて、全てのマスへの最短手数をマスごとに合計する。-1が一つでも混じっていたらそこは-1とし、無効扱いにする。最短手数の合計の全てのマスでの最小値が答え。全て-1だったら-1を返す。
import java.util.concurrent.ArrayBlockingQueue; public class CollectingRiders { public int minimalMoves(String[] board) { int n = board.length; int m = board[0].length(); char[][] b = new char[n][m]; for(int i = 0;i < n;i++){ b[i] = board[i].toCharArray(); } int[] dr = {2, 1, -1, -2, -2, -1, 1, 2}; int[] dc = {1, 2, 2, 1, -1, -2, -2, -1}; int[][] maxb = new int[n][m]; for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++){ if(b[i][j] >= '0' && b[i][j] <= '9'){ int k = b[i][j] - '0'; int[][] bb = new int[n][m]; ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(150); q.add(i*m+j); bb[i][j] = 1; while(!q.isEmpty()){ int cur = q.poll(); int r = cur/m; int c = cur%m; for(int l = 0;l < 8;l++){ int nr = r + dr[l]; int nc = c + dc[l]; if(nr >= 0 && nr < n && nc >= 0 && nc < m && bb[nr][nc] == 0){ bb[nr][nc] = bb[r][c] + 1; q.add(nr*m+nc); } } } for(int u = 0;u < n;u++){ for(int v = 0;v < m;v++){ if(maxb[u][v] != -1){ if(bb[u][v] == 0){ maxb[u][v] = -1; }else{ maxb[u][v] += (bb[u][v]-1+k-1)/k; } } } } } } } int min = 9999999; for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++){ if(maxb[i][j] != -1)min = Math.min(min, maxb[i][j]); } } return min == 9999999 ? -1 : min; } }
1000
2K(K<=50)個の、goodにかかれた数字からなる数字列がある。このうち次の性質のいずれかを満たすものは何通りあるか。999983で割った余りを答えよ。
- 前半のK個と後半のK個の数の合計が等しい。
- 奇数番目の項の和と偶数番目の項の和が等しい。
条件1をC1, 条件2をC2、条件Cを満たす数列の個数を#Cと表すと、
#C1+#C2-#(C1∧C2)
を求めれば良い。
goodにある数字からなるK個の数字の和がSであるようなK個の数字の組み合わせをm(K,S)とする。これ自体はDPで求められる。dp[i][j]=m(i,j)として、dp[i][j]=\sum_{k in good} dp[i-1][j-k]
とすればよい。
C1は、ある合計Sについて、前半のK個の和がS, 後半のK個の和がSになればいいので、
#C1=\sum_S m(K,S)^2
となる。Sの範囲はは0以上9*50=450以下。#C2も全く同じ値になる。
#(C1∧C2)を計算する。まずKが偶数のとき、前半で奇数番目にある項の数はK/2個、他、前半偶数番目、後半奇数番目、後半偶数番目もK/2個である。前半K個の和をS, 前半奇数番目の項の合計をAとするとき、条件1,2のどちらもみたすので、奇数・偶数番目の項の和もS. そして、条件1より、前半偶数番目の項の和はS-A, 条件2より後半奇数番目の項の和はS-A, 後半偶数番目の項の和はA. よって、
#(C1∧C2)=\sum_S \sum_A (m(K/2,A)m(K/2,S-A))^2
となる。Kが奇数の場合は、前半奇数番目、後半偶数番目の項の数が[K/2]+1になるだけなので、結局、
#(C1∧C2)=\sum_S \sum_A (m(K/2+K%2,A)m(K/2,S-A))^2
となる。あとは計算すれば求まる。
public class CharmingTicketsEasy { int MOD = 999983; public int count(int K, String good) { int fl = 0; for(int i = 0;i < good.length();i++){ fl |= 1<<(good.charAt(i) - '0'); } // K:odd K/2+1, K/2 // K:even K/2 int[][] dp = new int[K+1][451]; dp[0][0] = 1; for(int i = 1;i < K+1;i++){ for(int j = 0;j <= 450;j++){ int sum = 0; for(int k = 0;k <= 9;k++){ if((fl&1<<k)!=0 && j-k >= 0){ sum += dp[i-1][j-k]; } } dp[i][j] = sum % MOD; } } long one = 0; for(int ks = 0;ks <= 450;ks++){ one += ((long)dp[K][ks] * (long)dp[K][ks]) % MOD; } long inter = 0; for(int ks = 0;ks <= 450;ks++){ for(int a = 0;a <= ks;a++){ long uho = ((long)dp[K/2+(K%2)][a] * dp[K/2][ks-a]) % MOD; inter += (uho * uho) % MOD; } } return (int)((((one*2-inter) % MOD)+MOD)%MOD); } }