たこし++の備忘録

競技プログラミングの備忘録

SRM 669 Div1 Easy 250 SubdividedSlimes

問題概要

サイズSのスライムが与えられる。
以下の行動を繰り返す。
1:2以上のサイズsのスライムに注目する。
2:s=x+yとなるようにスライムをサイズxとyに分割する。(x > 0, y > 0)
3:得点としてx*yを得る。
4:1に戻る。
得点M以上を得るためには最小で何回スライムを分ければいいか
 S \le 1000  M \le 10^{9}

解法

嘘解法っぽいけど通ったので正義。

スライムは最大でS-1回しか分割することができないので、
t回の分割でどれだけ得点を最大化できるかを考える。

t = 3 としたとき、(直感的に)以下のように分割するのがよい

f:id:t-pro-6q:20150929001727p:plain
             ↓
f:id:t-pro-6q:20150929001731p:plain
             ↓
f:id:t-pro-6q:20150929001741p:plain
             ↓
f:id:t-pro-6q:20150929001747p:plain

エスパーなので証明は考えない。

ソース

メモ化再帰でやろうとしてたコードも残ってます

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <fstream>
#include <queue>
#include <complex>

using namespace std;

#define INF 1145141919
#define EPS 1e-9
#define PI acos(-1)
#define LL long long

LL gcd(LL a, LL b){ return (b) ? gcd(b, a%b) : a; }
LL lcm(LL a, LL b){ return (a*b) / gcd(max(a, b), min(a, b)); }

#define MAX_S 1001
#define MAX_TURN 1001

class SubdividedSlimes {
public:

	int memo[MAX_S][MAX_TURN];

	SubdividedSlimes(){
	
		memset(memo, -1, sizeof(memo));
	
	}

	int solve(int S, int turn){
	
		turn = min(S - 1, turn);

		cerr << S << " " << turn << endl;

		if (memo[S][turn] >= 0)
			return memo[S][turn];

		if (turn <= 0)
			return memo[S][turn] = 0;

		if (S == 1)
			return memo[S][turn] = 0;

		int ret = 0;

		for (int a = S / 2; a >= 1; a--){
		
			int b = S - a;

			int nextTurn = turn - 1;
			for (int i = 0; i <= nextTurn; i++){
				ret = max(ret, solve(a, i) + solve(b, nextTurn - i) + a*b);
			}
		
		}

		//cerr << S << " " << turn << " " << ret << endl;

		return memo[S][turn] = ret;
	
	}

	bool check(int S, int M, int cut){
	
		int num = S / (cut + 1);
		int numPlus = S % (cut + 1);

		int ans = 0;

		int a = num;
		if (numPlus > 0){
			a++;
			numPlus--;
		}

		while (cut > 0){
		
			cut--;

			int b = num;
			if (numPlus > 0){
				numPlus--;
				b++;
			}

			ans += a*b;
			a += b;
		
		}

		return ans >= M;
	
	}

	int needCut(int S, int M) {
		
		/*

		int left = 0, right = S+1;

		while (right - left > 1){
		
			int mid = (left + right) / 2;
			//cerr << mid << " " << solve(S, mid) << endl;
			if (solve(S, mid) >= M){
				right = mid;
			}
			else{
				left = mid;
			}
		
		}

		if (right >= S)
			return -1;
		else
			return right;

		*/

		for (int cut = 1; cut < S; cut++){
		
			if (check(S, M, cut))
				return cut;
		
		}

		return -1;

	}
};