TopCoder SRM 569 Div1 Medium TheJediTest

問題

n階立ての建物があって、i階目にはstudents[i]人の人がいる。


Jediは一人でK人の人を倒すことができる。
x人の人が一つの階にいるとき、x/K(切り上げ)人のJediが必要である。


それぞれのstudentsを、動かさないか、上下の隣り合う階に移すことができるとき、
建物の全てのstudentsを倒すのに、最小で何人のJediが要るか求めよ。

制約条件

n≦20
students[i]≦1億
1≦K≦1億


方針

それぞれの階と階との間で、移動するstudentsは一方の向きだけであるとしてよい。
(相殺できるから)


studentsの移動の向きを2^n-1通り決め付けて試す。
移動の向きが決まっているとき、
一番下の階から見ていけば、最適解は貪欲に決定できる。


(Kの倍数になるように調整して移動する。できない場合は、
決めた向きにしたがって、Kの余りで出来るだけ多い人数を移動させる)


O(n)の貪欲法でも解けるっぽい。

ソースコード

class TheJediTest {
	public:
	int minimumSupervisors(vector <int> students, int K) {
		int n = students.size();
		int ans = -1;
		
		vi old = students;
		
		rep(bit, 1 << n - 1){
			vector<ll> v;
			rep(i, n) v.pb(students[i]);
			
			rep(i, n - 1){
				if(bit & 1 << i){
					int tmp = min((K - v[i] % K) % K, (ll)old[i + 1]);
					v[i] += tmp;
					v[i + 1] -= tmp;
				}
				else{
					int tmp = min(v[i] % K, (ll)old[i]);
					v[i] -= tmp;
					v[i + 1] += tmp;
				}
			}
			ll s = 0;
			rep(i, n) s += (v[i] + K - 1) / K;
			if(ans < 0 || ans > s) ans = s;
		}
		return ans;
	}
};