AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC236-E Average and Median
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("6");
WillReturn.Add("2 1 2 1 1 10");
//4
//2
}
else if (InputPattern == "Input2") {
WillReturn.Add("7");
WillReturn.Add("3 1 4 1 5 9 2");
//5.250000000
//4
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] mAArr;
static long UB;
static void Main()
{
List<string> InputList = GetInputList();
mAArr = InputList[1].Split(' ').Select(pX => long.Parse(pX)).ToArray();
UB = mAArr.GetUpperBound(0);
decimal AVGMax = DeriveAvgMax();
Console.WriteLine(AVGMax);
long MedianMax = DeriveMedianMax();
Console.WriteLine(MedianMax);
}
// 平均の最大値を求める
static decimal DeriveAvgMax()
{
decimal L = 0;
decimal R = mAArr.Max();
if (CanAchieveAVG(R)) {
return R;
}
while (L + 0.00000001M < R) {
decimal Mid = (L + R) / 2;
if (CanAchieveAVG(Mid)) {
L = Mid;
}
else {
R = Mid;
}
}
return L;
}
// 平均をX以上にできるかを判定
static bool CanAchieveAVG(decimal pX)
{
// Max(シグマ(値 - X)) [現在Ind] なDP表
decimal?[] DPArr = new decimal?[UB + 1];
DPArr[0] = mAArr[0] - pX;
DPArr[1] = mAArr[1] - pX;
// 配るDP
for (long I = 0; I <= UB; I++) {
Action<long> UpdateAct = (pNewInd) =>
{
if (pNewInd > UB) return;
decimal NewVal = DPArr[I].Value + mAArr[pNewInd] - pX;
if (DPArr[pNewInd].HasValue) {
if (DPArr[pNewInd] >= NewVal) {
return;
}
}
DPArr[pNewInd] = NewVal;
};
UpdateAct(I + 1);
UpdateAct(I + 2);
}
if (DPArr[UB].Value >= 0) return true;
if (DPArr[UB - 1].Value >= 0) return true;
return false;
}
// 中央値の最大値を求める
static long DeriveMedianMax()
{
long L = 0;
long R = mAArr.Max();
if (CanAchieveMedian(R)) {
return R;
}
while (L + 1 < R) {
long Mid = (L + R) / 2;
if (CanAchieveMedian(Mid)) {
L = Mid;
}
else {
R = Mid;
}
}
return L;
}
// 中央値をX以上にできるかを判定
static bool CanAchieveMedian(long pX)
{
// 中央値との大小関係で、+1か-1に変換
long[] ChangedValArr = new long[UB + 1];
for (long I = 0; I <= UB; I++) {
if (mAArr[I] >= pX) {
ChangedValArr[I] = 1;
}
else {
ChangedValArr[I] = -1;
}
}
// Max(シグマ(値)) [現在Ind] なDP表
long?[] DPArr = new long?[UB + 1];
DPArr[0] = ChangedValArr[0];
DPArr[1] = ChangedValArr[1];
// 配るDP
for (long I = 0; I <= UB; I++) {
Action<long> UpdateAct = (pNewInd) =>
{
if (pNewInd > UB) return;
long NewVal = DPArr[I].Value + ChangedValArr[pNewInd];
if (DPArr[pNewInd].HasValue) {
if (DPArr[pNewInd] >= NewVal) {
return;
}
}
DPArr[pNewInd] = NewVal;
};
UpdateAct(I + 1);
UpdateAct(I + 2);
}
if (DPArr[UB].Value >= 1) return true;
if (DPArr[UB - 1].Value >= 1) return true;
return false;
}
}
解説
平均は、
シグマ(値) / 個数 ですが
シグマ(値) / シグマ(1)
と考えることができます。
平均をX以上にできるかという不等式は、
シグマ(値) / シグマ(1) >= X
で変形すると
シグマ(値) >= シグマ(X)
シグマ(値 - X) >= 0
で、
Max(シグマ(値 - X)) [現在Ind] なDP表
を更新するDPを、二分法のCanAchieveメソッドで行うことができます。
中央値に関しては、
中央値以上の値を+1
中央値未満の値を-1
として、同様のDPを、二分法のCanAchieveメソッドで行うことができます。