BottomCoder SRM 370 div2

SRM 370 div2

points result time
250 AC 2.7m
500 AC 4.4m
1000 AC 46.5m

でたー!T9だー!

250

containers(||<=50)のサイズのコンテナににpackages(||<=50)のサイズのパッケージを、前から貪欲に入れて収まりきれなくなったら次のコンテナへ、という風にいれる。全てのパッケージが収まりきることがわかっているとき、収めた後のコンテナの空いている空間のサイズの合計を求めよ。

え、ただたすだけじゃん?とか思ってなんども見直した。

import java.util.Arrays;

public class Containers {
	public int wastedSpace(int[] containers, int[] packages) {
		int s = 0;
		for(int c : containers){
			s += c;
		}
		for(int p : packages){
			s -= p;
		}
		return s;
	}

	void tr(Object... o) { System.out.println(Arrays.deepToString(o)); }
}

500

箱のなかにある各色のボールの個数がcolors(||<=50)で与えられる。箱からランダムにn(<=sum colors[*])個のボールを取り出すとき、それらが全て同じ色になっている確率を求めよ。

簡単な確率の問題。S=sum colors[*]とすると、
sum_i C(colors[i], n)/C(S,n)
となる。

BigIntegerで計算しているが、別にdoubleでも良い。

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Arrays;

public class DrawingMarbles {
	public double sameColor(int[] colors, int n) {
		int sum = 0;
		for(int c : colors){
			sum += c;
		}
		BigInteger den = BigInteger.ONE;
		for(int i = 0;i < n;i++){
			den = den.multiply(BigInteger.valueOf(sum-i));
		}
		BigInteger num = BigInteger.ZERO;
		for(int c : colors){
			if(c >= n){
				BigInteger lnum = BigInteger.ONE;
				for(int i = 0;i < n;i++){
					lnum = lnum.multiply(BigInteger.valueOf(c-i));
				}
				num = num.add(lnum);
			}
		}
		return new BigDecimal(num).divide(new BigDecimal(den), 20, RoundingMode.HALF_UP).doubleValue();
	}

	void tr(Object... o) { System.out.println(Arrays.deepToString(o)); }
}

1000

T9の入力方式で文字列message(||<=50)を最短ストロークで入力したい。T9の入力方式は以下のとおり。a-zに合計8通りのキーが割り当てられ、このキーをk個入力すると、同じキーストロークで始まる辞書(||<=2500/3?)内の単語のうち一番最初のものの、最初のk文字が表示される。"Next in dictionary"を押すと、辞書内で該当する次の単語の最初のk文字が表示される。また、"New word"を押すと表示中の文字はすべて確定される。スペースを押すと表示中の文字を全て確定した上にスペース文字を追加する。最短ストローク数を答えよ。どうやっても打てない場合は-1を答えよ。ただし終了時に確定されている必要はない。

実装+DPといういやらしいコンビ。最近SRM490のdiv1mediumで類題が出た。やり方は基本的に同じ。

全確定状態から全確定状態に移行する際に増える文字列は、かならず辞書内の文字列のprefix全体の集合のどれかの要素になっている。まずは、このprefixをうつための最短ストローク数を求める。ある辞書内の単語Wのprefix Pについて、これをうつためのストローク数は、(Pと同じキーストロークのprefixを持つ、辞書内でWより前にある単語の個数)+(Pの文字数)で与えられる。同じ字面のprefixのなかでは最小のものを選んでおく。

次にDPをする。dp[i]=(messageのi-1文字目まで入力するまでの最短ストローク数)とする。i-1文字目がスペースのときはdp[i]=dp[i-1]+1となる。
i-1文字目がスペース以外の場合、0<=j

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class JohnnysPhone {
	int INF = Integer.MAX_VALUE / 10;
	
	public int minimizePushes(String[] dictionary, String message) {
		StringBuilder sb = new StringBuilder();
		for(String w : dictionary)sb.append(w);
		String[] dic = sb.toString().split(" ");
		int m = dic.length;
		String[] td = new String[m];
		for(int i = 0;i < m;i++){
			td[i] = trans(dic[i]);
		}
		
		Map<String, Integer> mins = new HashMap<String, Integer>();
		for(int i = 0;i < m;i++){
			String w = dic[i];
			for(int j = 1;j <= w.length();j++){
				String sub = w.substring(0, j);
				String subt = td[i].substring(0, j);
				int ct = 0;
				for(int k = 0;k < i;k++){
					if(dic[k].length() >= j && subt.equals(td[k].substring(0, j))){
						ct++;
					}
				}
				if(!mins.containsKey(sub))mins.put(sub, INF);
				mins.put(sub, Math.min(ct+j, mins.get(sub)));
			}
		}
//		tr(mins);
		
		int n = message.length();
		int[] dp = new int[n+1];
		dp[0] = 0;
		for(int i = 1;i <= n;i++){
			int min = INF;
			if(message.charAt(i-1) == ' '){
				dp[i] = dp[i-1]+1;
			}else{
				for(int j = 0;j < i;j++){
					String u = message.substring(j, i);
					if(mins.containsKey(u)){
//						tr(j, i, u, mins.get(u));
						min = Math.min(min, dp[j]+(j==0 || message.charAt(j-1) == ' ' ? 0:1)+mins.get(u));
					}
				}
				dp[i] = min;
			}
		}
//		tr(dp);
		
		return dp[n] >= INF ? -1 : dp[n];
	}
	
	String trans(String x)
	{
		StringBuilder nw = new StringBuilder();
		for(int j = 0;j < x.length();j++){
			if(x.charAt(j) == ' '){
				nw.append(' ');
			}else{
				nw.append(AL[x.charAt(j)-'a']);
			}
		}
		return nw.toString();
	}
	
	char[] AL = "22233344455566677778889999".toCharArray();

	void tr(Object... o) { System.out.println(Arrays.deepToString(o)); }
}