AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC247-E Max Min
C#のソース(DPで解く方法)
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 3 1");
WillReturn.Add("1 2 3 1");
//4
}
else if (InputPattern == "Input2") {
WillReturn.Add("5 2 1");
WillReturn.Add("1 3 2 4 1");
//0
}
else if (InputPattern == "Input3") {
WillReturn.Add("5 1 1");
WillReturn.Add("1 1 1 1 1");
//15
}
else if (InputPattern == "Input4") {
WillReturn.Add("10 8 1");
WillReturn.Add("2 7 1 8 2 8 1 8 2 8");
//36
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static void Main()
{
List<string> InputList = GetInputList();
int[] wkArr = InputList[0].Split(' ').Select(pX => int.Parse(pX)).ToArray();
int MaxVal = wkArr[1];
int MinVal = wkArr[2];
int[] AArr = InputList[1].Split(' ').Select(pX => int.Parse(pX)).ToArray();
// 区間完了の場合の数[Maxが登場,Minが登場]
int[,] PrevDP = new int[2, 2];
long Answer = 0;
foreach (int EachA in AArr) {
if (EachA < MinVal || MaxVal < EachA) {
PrevDP = new int[2, 2];
continue;
}
// 貰うDP
int[,] CurrDP = new int[2, 2];
int FirstI = 0;
int FirstJ = 0;
if (EachA == MaxVal) FirstI = 1;
if (EachA == MinVal) FirstJ = 1;
CurrDP[FirstI, FirstJ]++;
for (int I = 0; I <= 1; I++) {
for (int J = 0; J <= 1; J++) {
int NewI = I;
int NewJ = J;
if (EachA == MaxVal) NewI = 1;
if (EachA == MinVal) NewJ = 1;
CurrDP[NewI, NewJ] += PrevDP[I, J];
}
}
// 解を計上
Answer += CurrDP[1, 1];
PrevDP = CurrDP;
}
Console.WriteLine(Answer);
}
}
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 3 1");
WillReturn.Add("1 2 3 1");
//4
}
else if (InputPattern == "Input2") {
WillReturn.Add("5 2 1");
WillReturn.Add("1 3 2 4 1");
//0
}
else if (InputPattern == "Input3") {
WillReturn.Add("5 1 1");
WillReturn.Add("1 1 1 1 1");
//15
}
else if (InputPattern == "Input4") {
WillReturn.Add("10 8 1");
WillReturn.Add("2 7 1 8 2 8 1 8 2 8");
//36
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int mMaxVal;
static int mMinVal;
static void Main()
{
List<string> InputList = GetInputList();
int[] wkArr = InputList[0].Split(' ').Select(pX => int.Parse(pX)).ToArray();
mMaxVal = wkArr[1];
mMinVal = wkArr[2];
int[] AArr = InputList[1].Split(' ').Select(pX => int.Parse(pX)).ToArray();
var ValArrList = new List<int[]>();
var ValList = new List<int>();
for (int I = 0; I <= AArr.GetUpperBound(0); I++) {
if (AArr[I] < mMinVal || mMaxVal < AArr[I]) {
if (ValList.Count > 0) {
ValArrList.Add(ValList.ToArray());
ValList.Clear();
}
continue;
}
ValList.Add(AArr[I]);
if (I == AArr.GetUpperBound(0)) {
ValArrList.Add(ValList.ToArray());
}
}
long Answer = 0;
foreach (int[] EachArr in ValArrList) {
//Console.WriteLine(IntEnumJoin(",", EachArr));
Answer += DerivePatternCnt(EachArr);
}
Console.WriteLine(Answer);
}
////////////////////////////////////////////////////////////////
// セパレータとInt型の列挙を引数として、結合したstringを返す
////////////////////////////////////////////////////////////////
static string IntEnumJoin(string pSeparater, IEnumerable<int> pEnum)
{
string[] StrArr = Array.ConvertAll(pEnum.ToArray(), pX => pX.ToString());
return string.Join(pSeparater, StrArr);
}
// 配列ごとの場合の数を返す
static long DerivePatternCnt(int[] pArr)
{
long WillReturn = 0;
int UB = pArr.GetUpperBound(0);
var MaxValIntList = new List<int>();
var MinValIntList = new List<int>();
for (int I = 0; I <= UB; I++) {
if (pArr[I] == mMaxVal) MaxValIntList.Add(I);
if (pArr[I] == mMinVal) MinValIntList.Add(I);
}
if (MaxValIntList.Count == 0) return 0;
if (MinValIntList.Count == 0) return 0;
// 左端を全探索
for (int I = 0; I <= UB; I++) {
int LowerB1 = ExecNibunhou_LowerBound(I, MaxValIntList);
int LowerB2 = ExecNibunhou_LowerBound(I, MinValIntList);
if (LowerB1 == -1) continue;
if (LowerB2 == -1) continue;
int RMin = Math.Max(MaxValIntList[LowerB1], MinValIntList[LowerB2]);
//Console.WriteLine("左端が{0}での場合の数={1}", I, UB - RMin + 1);
WillReturn += UB - RMin + 1;
}
return WillReturn;
}
// 二分法で、Val以上で最小の値を持つ、添字を返す (List版)
static int ExecNibunhou_LowerBound(int pVal, List<int> pList)
{
// 最後の要素がVal未満の特殊ケース
if (pVal > pList.Last()) {
return -1;
}
// 最初の要素がVal以上の特殊ケース
if (pVal <= pList[0]) {
return 0;
}
int L = 0;
int R = pList.Count - 1;
while (L + 1 < R) {
int Mid = (L + R) / 2;
if (pList[Mid] >= pVal) {
R = Mid;
}
else {
L = Mid;
}
}
return R;
}
}
解説
最小以上最大以下の連続した区間で分割して考えます。
最小値が5、最大値が10で
分割済の配列の1つが
6 7 5 8 10 9 5 9 10
だったとすると
左端ごとに、最小があるLowerBと、最大があるLowerBが分かれば、
max(最小があるLowerB , 最大があるLowerB) から 配列の最後のInd
が可能な区間の右端なので
区間の左端ごとの、可能な区間長が分かります。