BottomCoder SRM 393 div2

SRM 393 div2

points result time
250 AC ?m(数分だが短くはない)
500 AC 6.5m
1000 AC 21m

DP・・もっと速く・・

250

長さjourneyLength(<=100000)の道を、speedLimit(||<=50)に記された速度パターンを繰り返して走るときの時間を求めよ。i番目では、1時間単位につき、speedLimit[i]だけ走る。

問題文を簡潔に書き起こしてもニュアンスが伝わりにくい・・問題文を理解するのに時間がかかった問題。
speedLimit[i]は1以上で、journeyLength<=100000なので、端折らなくてもループするだけで余裕で終わる。

import java.util.Arrays;

public class VariableSpeedLimit {

	public double journeyTime(int journeyLength, int[] speedLimit) {
		double time = 0;
		int n = speedLimit.length;
		while(true){
			for(int i = 0;i < n;i++){
				if(journeyLength >= speedLimit[i]){
					journeyLength -= speedLimit[i];
					time++;
				}else{
					time += (double)journeyLength / speedLimit[i];
					return time;
				}
			}
		}
	}

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

500

N(<=50)人のvotersがM(<=10)人の候補者に対し投票を行う。各votersは0〜M-1の置換の形で投票する候補者の優先順を決めている。投票の結果、過半数(半数は含まない)を獲得した候補者がいればその候補者の番号を返せ。そのような候補者が居ない場合は、最低得票数の候補者を全員ポアしたのち、再投票を行う。これを繰り返した結果、誰も当選せず全員ポアされた場合は-1を返せ。

実装ゲー。制約が小さいのでそのまま実装すればよい。候補者が10人しかいないのでbitでやってみました。
余談だけど、Java,AS3での演算子の優先順位ではどちらも
<<,>> > ==,!= > &
になっていたので、xのnビット目を調べるコードは括弧一つ減らして(x&1<

import java.util.Arrays;

public class InstantRunoffVoting {

	public int winner(String[] voters) {
		int n = voters.length;
		int m = voters[0].length();
		int b = (1<<m)-1;
		
		while(b != 0){
			int[] ct = new int[m];
			for(int i = 0;i < n;i++){
				for(int j = 0;j < m;j++){
					int c = voters[i].charAt(j) - '0';
					if((b&(1<<c))!=0){
						ct[c]++;
						break;
					}
				}
			}
			
			int mins = 0;
			int min = 99999;
			for(int i = 0;i < m;i++){
				if((b&(1<<i))!=0){
					if(ct[i] > n / 2){
						return i;
					}
					if(min > ct[i]){
						min = ct[i];
						mins = 1<<i;
					}else if(min == ct[i]){
						mins |= 1<<i;
					}
				}
			}
			b &= ~mins;
		}
		
		return -1;
	}

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

1000

N(<=50*2+16+1)頂点M(<=50)辺の有向グラフが与えられる。ある頂点vと頂点集合S(||<=16)が与えられ、vからSのすべての要素へのパスが存在するように辺を追加する。追加する辺の最小本数を求めよ。

文章題の皮を剥ぐとだいたいこんな感じ。問題文長い上に文字列処理めんどい。頂点集合の要素数が20より小さいのでbitです。

まず頂点同士がたどれるかどうかを調べるために、Warshall-Floydを有向グラフにかける。これに自分自身への対応を加えた後、各頂点からSの各要素に辿りつけるかどうかをbitで表す。これをstateと呼ぶことにする。
vからある頂点aへ辺を結ぶと、vから行けるstateはstate(v)|state(a)となる。vを介さない辺を新たに結んでも、全体の本数的には、vからその辺の両端にそれぞれ結ぶのと変わらないので、追加する辺はすべてvから出るとしてよく、辺のもう一方の端となる頂点の個数をできるだけすくなくして、state=(1<<|S|)-1となればよい。
vを0番目の頂点として、
dp[i][j]=(i番目の頂点まで見たときに、vのstateがjとなっているものの辺の最小本数)としてDPすればよい。漸化式は
dp[i+1][j|state[i+1] ]=min(dp[i+1][j|state[i+1] ],dp[i][j])と
dp[i+1][j]=min(dp[i+1][j],dp[i][j]).
後者の式があるので、dpの1項目を省いて使い回せば行ける?*1
初期化はdp[0][state[0] ]=0, dp[0][*]=INFTY
dp[N][(1<<|S|)-1]が答え。これはINFTYにはならない。

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

public class PowerAdapters {

	public int needed(String[] adapters, String[] itinerary, String homeCountry) {
		Map<String, Integer> map = new HashMap<String, Integer>();
		int p = 0;
		if(!map.containsKey(homeCountry))map.put(homeCountry, p++);
		for(int i = 0;i < adapters.length;i++){
			String[] sp = adapters[i].split(" ");
			if(!map.containsKey(sp[0]))map.put(sp[0], p++);
			if(!map.containsKey(sp[1]))map.put(sp[1], p++);
		}
		for(String it : itinerary){
			if(!map.containsKey(it))map.put(it, p++);
		}
		boolean[][] g = new boolean[p][p];
		for(int i = 0;i < adapters.length;i++){
			String[] sp = adapters[i].split(" ");
			g[map.get(sp[0])][map.get(sp[1])] = true;
		}
		int[] its = new int[itinerary.length];
		for(int i = 0;i < itinerary.length;i++){
			its[i] = map.get(itinerary[i]);
		}
		
		for(int k = 0;k < p;k++){
			for(int i = 0;i < p;i++){
				for(int j = 0;j < p;j++){
					g[i][j] |= g[i][k] && g[k][j];
				}
			}
		}
		for(int i = 0;i < p;i++){
			g[i][i] = true;
		}
		
		bt = new int[p];
		for(int i = 0;i < p;i++){
			for(int j = 0;j < its.length;j++){
				if(g[i][its[j]])bt[i] |= 1<<j;
			}
		}
		
		int m = its.length;
		int[][] dp = new int[p][1<<m];
		for(int i = 0;i < p;i++){
			Arrays.fill(dp[i], 99999);
		}
		dp[0][bt[0]] = 0;
		
		for(int i = 0;i < p - 1;i++){
			for(int j = 0;j < 1<<m;j++){
				dp[i+1][j] = Math.min(dp[i+1][j], dp[i][j]);
				dp[i+1][j|bt[i+1]] = Math.min(dp[i+1][j|bt[i+1]], dp[i][j]+1);
			}
		}
		
		return dp[p-1][(1<<m)-1];
	}
	
	
	
	int[] bt;

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

*1:jを降順に走査してdp[j|state[i+1] ]=min(dp[j|state[i+1],dp[j])かな?