AtCoderのPAST
次のPASTの問題へ
前のPASTの問題へ
第6回PAST N 活動
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("4 6");
WillReturn.Add("4 1");
WillReturn.Add("3 2");
WillReturn.Add("2 3");
WillReturn.Add("1 4");
//45
}
else if (InputPattern == "Input2") {
WillReturn.Add("4 6");
WillReturn.Add("1 1");
WillReturn.Add("2 2");
WillReturn.Add("3 3");
WillReturn.Add("4 4");
//30
}
else if (InputPattern == "Input3") {
WillReturn.Add("16 100");
WillReturn.Add("18 17");
WillReturn.Add("5 18");
WillReturn.Add("7 2");
WillReturn.Add("5 8");
WillReturn.Add("6 2");
WillReturn.Add("16 16");
WillReturn.Add("2 18");
WillReturn.Add("13 17");
WillReturn.Add("18 10");
WillReturn.Add("11 10");
WillReturn.Add("17 8");
WillReturn.Add("1 2");
WillReturn.Add("20 7");
WillReturn.Add("4 11");
WillReturn.Add("7 15");
WillReturn.Add("2 1");
//9282
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
////////////////////////////////////////////////////////////////
// ソートを定義した構造体
////////////////////////////////////////////////////////////////
struct ActInfoDef : IComparable<ActInfoDef>
{
internal long Score;
internal long Cost;
public int CompareTo(ActInfoDef pOtherIns)
{
long Sahen = Score * pOtherIns.Cost;
long Uhen = Cost * pOtherIns.Score;
return Uhen.CompareTo(Sahen);
}
}
static List<ActInfoDef> mActInfoList = new List<ActInfoDef>();
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(pX => long.Parse(pX)).ToArray();
SplitAct(InputList[0]);
long HP = wkArr[1];
foreach (string EachStr in InputList.Skip(1)) {
SplitAct(EachStr);
ActInfoDef WillAdd;
WillAdd.Score = wkArr[0];
WillAdd.Cost = wkArr[1];
mActInfoList.Add(WillAdd);
}
mActInfoList.Sort();
var AnswerList = new List<long>();
// 1個しか選ばない場合
AnswerList.Add(HP * mActInfoList.Max(pX => pX.Score));
// 複数選ぶ場合は、01ナップサック問題
long UB = HP;
// 最大スコア[HP]なインラインDP表
long?[] DPArr = new long?[UB + 1];
DPArr[HP] = 0;
foreach (ActInfoDef EachActInfo in mActInfoList) {
for (long I = 1; I <= UB; I++) {
if (DPArr[I].HasValue == false) continue;
long NewI = I - EachActInfo.Cost;
long NewVal = DPArr[I].Value + I * EachActInfo.Score;
AnswerList.Add(NewVal);
if (NewI <= 0) {
continue;
}
if (DPArr[NewI].HasValue) {
if (DPArr[NewI].Value >= NewVal) {
continue;
}
}
DPArr[NewI] = NewVal;
}
}
Console.WriteLine(AnswerList.Max());
}
}
解説
場合に分けて考えます。
1つの活動のみ行う場合
この場合は、aが最大の活動を行うのが最適です。
2つ以上の活動を行う場合
今の体力をHP
活動1を{a1,b1}
活動2を{a2,b2}
とし、
a1 * HP + a2*(HP-b1) >= a2 * HP + a1*(HP-b2)
⇔
-a2*b1 >= -a1*b2
⇔
a1*b2 >= a2*b1
⇔
a1 / b1 >= a2 / b2
なので
a1/b1 の降順に活動するのが最適だと分かります。
後は、ソートを定義できる構造体でa1/b1 の降順に
誤差を気を付けてソートし、
最大スコア[現在HP]を更新するインラインDPで解けます。