SRM340

250

与えられた数字Nをつなげていったときに最低何個つなげたらkの倍数になりますかという問題.できなかったら-1を返す.
数字をつなげるというのはNの長さをlとしたときに10^lかけて,Nを足すのと同じなのでmod kで考えていって0になるまで繰り返す.ただし,同じ数字が出てきたら巡回したので-1を返す.

500

単純なDPで解こうとするとO(2^N M^2 N)でNが15まででMが50までなので計算時間が間に合わなくなる.最適化するといけるみたいだけど.そこで許すコストを固定した時にはO(2^N N)で計算できるのを利用してコストに関して2分探索するといけるみたい.

追記 :
500をとりあえず書いてみた,O(2^N N)で書けるはずだけどメモ化をさぼったO(2^N NM)のコードでも間に合う

public class PaintingBoards {
	int bl[] , ps[];
	public double minimalTime(int[] boardLength, int[] painterSpeed) {
		bl = boardLength;
		ps = painterSpeed;
		double low = 0.0;
		double high = 500000.0;
		while(high - low >= 1.0E-9){
			double middle = low + (high - low) / 2.0;
			if(can(middle)) high = middle;
			else low = middle;
		}
		return low;
	}
	boolean can(double time){
		int dp[] = new int[1<<ps.length];
		for(int i = 0 ; i < ps.length ; i++){
			for(int j = 0 , s = 0; j < bl.length ; j++){
				s += bl[j];
				if(s  > time * ps[i])break;
				else dp[1<<i] = j + 1;
			}
		}		
		for(int k = 1 ; k < dp.length ; k++)
			for(int i = 0 ; i < ps.length ; i++)
				if((k & (1 <<i))!= 0){
					int prev = dp[k - (1<<i)];
					int res = prev;
					for(int j = prev , s = 0; j < bl.length ; j++){
						s += bl[j];
						if(s > time * ps[i])break;
						else res = j + 1;
					}
					dp[k] = Math.max(dp[k], res);
				}
		
		for(int i : dp)if(i==bl.length)return true;
		return false;
	}
}