Problem:
問題名 Audition

アイドル---それは女の子達の永遠の憧れ。しかし、頂点に立てるのはほんの一握り。そんなサバイバルな世界に、あなたはアイドルプロデューサーとして足を踏み入れることになりました。そして、今日はあなたの担当アイドルを連れて大事なオーディションに挑むことになりました。

オーディションの決め手になるのはビジュアル、ダンス、ボーカルの 3 要素です。オーディション中には m 回のアピールタイムがあり、 1 回のアピールタイムごとに各アイドルはビジュアルアピール、ダンスアピール、ボーカルアピールのいずれかを行うことが出来ます。ビジュアルアピールを行うとそのアイドルのビジュアルポイントは彼女の持ってるビジュアル値だけ、ダンスアピールを行うとダンスポイントは彼女の持ってるダンス値だけ、ボーカルアピールを行うとボーカルポイントは彼女の持ってるボーカル値だけ上昇します。

m 回のアピールが終了した後、N 人のアイドルのうちビジュアルポイントの高いほうから 3 人は 5 オーディションポイント、ダンスポイントの高いほうから 3 人は 3 オーディションポイント、ボーカルポイントの高いほうから 3 人は 2 オーディションポイントを獲得します。一方、ビジュアルポイントが最下位だとオーディションポイントは 1 点減点、ダンスポイントが最下位だとオーディションポイントは 1 点減点、ボーカルポイントが最下位だとオーディションポイントは 1 点減点となります。（オーディションポイントが1以下でもそこから 1 点減点）。

オーディションの始めに、各アイドルのビジュアルポイント、ダンスポイント、ボーカルポイントはそれぞれ 0 です。あなたはアイドル 1 のプロデューサーで、アピールタイムごとにどのアピールを行うか指示することが出来ます。アイドル 1 以外のアイドルはアピールタイムごとにランダムに等確率にどれかのアピールを行います（つまりそれぞれ 3 分の 1 の確率で行われる）。アイドル 1 のビジュアル、ダンス、ボーカルのポイントが他のアイドルと等しい場合、アイドル 1 は常に下位とします。アイドル 1 の獲得するオーディションポイントの期待値が最大になるように指示したときの期待値を求めて下さい。アイドルの運命は、あなたの指示にかかっています！

ちなみに、指示はオーディションが始まる前に全て行って、その後オーディション中の指示の変更は出来ないものとします。つまり、他のアイドルが、オーディションが始まってからのアピールでどのアピールをしかたを見てから、その場合においての期待値が最大となるような指示を出す、ということは出来ないものとします。

Input

入力は以下の形で与えられます。

n m
vi1 da1 vo1
vi2 da2 vo2
...
vin dan von

1行目にはオーディションに出場するアイドルの数 n (4 ≤ n ≤ 2000) と、このオーディションにおけるアピールタイムの数 m (1 ≤ m ≤ 2000) が書いてあります。次の n 行にはそれぞれアイドルiのビジュアル値 vii、ダンス値 dai、ボーカル値 voi (1 ≤ vii, dai, voi ≤ 10,000)が書いてあります。

Output

アイドル 1 の獲得するオーディションポイントの期待値の最大値を 1 行で出力しなさい。

Sample Input 1

4 1
1 1 1
1 1 1
1 1 1
1 1 1

Output for the Sample Input 1

2.777777777778

どのアピールを行っても、そのジャンルで 3 位以内に入れる確率は等しいですが、 3 位以内に入った時に貰えるオーディションポイントが最も多いビジュアルのアピールを行うのがもっとも期待値が良くなります。

Sample Input 2

4 10
1 1 1
10 10 10
10 10 10
10 10 10

Output for the Sample Input 2

-2.335340954780

非常に厳しいオーディションとなるでしょう。

Sample Input 3

9 9
13 5 19
19 21 37
15 1 7
7 11 15
21 23 25
33 29 19
13 19 11
21 5 15
7 13 1

Output for the Sample Input 3

4.678837855075

Solution in C (judge verdict: Accepted)

// AOJ 2434: Audition
// 2017.12.30 bal4u@uu

#include <stdio.h>

#define A 0.66666666666666666666666666666667

int n, m;
int vi[2002], da[2002], vo[2002];
double s[2002], e[3][2002];
double dp[4][2002]; char mk[4][2002];
char buf[25], *p;

int in()
{
	int n = 0;
	while (*p >= '0') n = 10*n + (*p++ & 0xf);
	p++;
	return n;
}

void calc(double *e, int *a, int x)
{
	int i, j, k, t;
	double p, q[4], r;

	for (i = 0; i <= m; i++) {
		p = 1, q[0] = 1, q[1] = q[2] = q[3] = 0;
		for (j = 1; j < n; j++) {
			t = (a[0] * i + a[j] - 1)/a[j] - 1;
			r = 0;
			if (t >= 0) if (t < m) r = s[t]; else r = s[m];

			p *= 1-r;
			for (k = 2; k >= 0; k--) q[k+1] += q[k] * (1-r), q[k] *= r;
		}
		e[i] = (q[0] + q[1] + q[2]) * x - p;
	}
}

int main()
{
	int i, j, k;
	
	fgets(p=buf, 15, stdin), n = in(), m = in();

	s[0] = 1;
	for (i = 0; i < m; i++) {
		for (j = i; j >= 0; j--) s[j+1] += s[j] / 3, s[j] *= A;
	}
	for (i = 0; i < m; i++) s[i+1] += s[i];

	for (i = 0; i < n; i++) {
		fgets(p=buf, 25, stdin);
		vi[i] = in(), da[i] = in(), vo[i] = in();
	}
	calc(e[0], vi, 5), calc(e[1], da, 3), calc(e[2], vo, 2);

	dp[0][0] = 0, mk[0][0] = 1;
	for (i = 0; i < 3; i++) for (j = 0; j <= m; j++) if (mk[i][j]) {
		for (k = 0; k <= m-j; k++) {
			double t = dp[i][j] + e[i][k];
			if (mk[i+1][j+k] && dp[i+1][j+k] > t) t = dp[i+1][j+k];
			dp[i+1][j+k] = t, mk[i+1][j+k] = 1;
		}
	}
	printf("%.9f\n", dp[3][m]);
	return 0;
}