AtCoderのARC
前のARCの問題へ
ARC223-A Unusual-Constraint Knapsack
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("3");
WillReturn.Add("3 10");
WillReturn.Add("1 2");
WillReturn.Add("3 1");
WillReturn.Add("9 3");
WillReturn.Add("3 20");
WillReturn.Add("1 2");
WillReturn.Add("3 1");
WillReturn.Add("9 3");
WillReturn.Add("4 15");
WillReturn.Add("1 1");
WillReturn.Add("3 2");
WillReturn.Add("5 3");
WillReturn.Add("10 4");
//5
//6
//7
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
struct ItemInfoDef
{
internal long Weight;
internal long Value;
}
static List<ItemInfoDef> mItemInfoList = new List<ItemInfoDef>();
static ItemInfoDef[] mItemInfoArr;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);
int CurrInd = 1;
while (true) {
if (CurrInd > InputList.Count - 1) break;
SplitAct(InputList[CurrInd]);
long N = wkArr[0];
long W = wkArr[1];
mItemInfoList.Clear();
for (int I = CurrInd + 1; I <= CurrInd + 1 + N - 1; I++) {
SplitAct(InputList[I]);
ItemInfoDef WillAdd;
WillAdd.Weight = wkArr[0];
WillAdd.Value = wkArr[1];
mItemInfoList.Add(WillAdd);
}
mItemInfoArr = mItemInfoList.ToArray();
long Answer = Solve(W);
Console.WriteLine(Answer);
CurrInd += (int)N + 1;
}
}
static long Solve(long pWeight)
{
long RestWeight = pWeight;
var WeightList = new List<long>();
for (long I = 0; I <= mItemInfoArr.GetUpperBound(0); I++) {
WeightList.Add(mItemInfoArr[I].Weight);
}
var AnswerList = new List<long>();
AnswerList.Add(0);
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.CurrInd = WeightList.Count - 1;
WillPush.RestWeight = pWeight;
WillPush.ScoreSum = 0;
Stk.Push(WillPush);
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
// 取得可能な最大重さなアイテムを求める
int ResultInd = ExecNibunhou_LowerOrEqual_Max(Popped.RestWeight, WeightList);
if (ResultInd == -1) {
continue;
}
ResultInd = Math.Min((int)Popped.CurrInd, ResultInd);
// 取る場合
WillPush.CurrInd = ResultInd - 1;
WillPush.RestWeight = Popped.RestWeight - mItemInfoArr[ResultInd].Weight;
WillPush.ScoreSum = Popped.ScoreSum + mItemInfoArr[ResultInd].Value;
AnswerList.Add(WillPush.ScoreSum);
if (WillPush.CurrInd >= 0) {
Stk.Push(WillPush);
}
// 取らない場合は、残りを全部取る
if (ResultInd >= 1) {
long RangeSum = GetRangeSum(0, ResultInd - 1);
AnswerList.Add(Popped.ScoreSum + RangeSum);
}
}
return AnswerList.Max();
}
struct JyoutaiDef
{
internal long CurrInd;
internal long RestWeight;
internal long ScoreSum;
}
static long GetRangeSum(long pRangeSta, long pRangeEnd)
{
long RangeSum = 0;
for (long I = pRangeSta; I <= pRangeEnd; I++) {
RangeSum += mItemInfoArr[I].Value;
}
return RangeSum;
}
// 二分法で、Val以下で最大の値を持つ、添字を返す
static int ExecNibunhou_LowerOrEqual_Max(long pVal, List<long> pList)
{
if (pList.Count == 0) return -1;
// 最後の要素がVal以下の特殊ケース
if (pVal >= pList.Last()) {
return pList.Count - 1;
}
// 最初の要素がVal超えの特殊ケース
if (pVal < pList[0]) {
return -1;
}
int L = 0;
int R = pList.Count - 1;
while (L + 1 < R) {
int Mid = (L + R) / 2;
if (pList[Mid] <= pVal) {
L = Mid;
}
else {
R = Mid;
}
}
return L;
}
}
解説
重さの制約をふまえ、分かりやすく10進数で考えます。
重さ 1 10 100 1000 10000 100000
価値 3 1 4 1 5 9
{ナップサックの残り容量 , 荷物をどこまで見たか , 価値合計}
を状態に持ち、以下のDFSで解けます。
まだ、取ってない荷物の中で、重さが最大の荷物を、見つける。
この荷物を取るなら、DFSを続ける。
この荷物を取らないなら、残りの全部の荷物を取り、DFSを枝切り