AtCoderのARC
次のARCの問題へ
前のARCの問題へ
ARC157-B XYYYX
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("5 1");
WillReturn.Add("XYXYX");
//2
}
else if (InputPattern == "Input2") {
WillReturn.Add("5 4");
WillReturn.Add("XYXYX");
//2
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => int.Parse(pX)).ToArray();
}
static void Main()
{
List<string> InputList = GetInputList();
int[] wkArr = GetSplitArr(InputList[0]);
int K = wkArr[1];
string S = InputList[1];
int Result = Solve(S, K);
Console.WriteLine(Result);
}
static int Solve(string pS, int pK)
{
int UB = pS.Length - 1;
int XCnt = pS.ToCharArray().Count(pX => pX == 'X');
int YCnt = pS.ToCharArray().Count(pX => pX == 'Y');
int ChangeCnt = Math.Min(pK, XCnt);
pK -= ChangeCnt;
HashSet<int> ChangeIndSet1 = DeriveChangeIndSet(pS, ChangeCnt);
char[] SArr = pS.ToCharArray();
for (int I = 0; I <= UB; I++) {
if (ChangeIndSet1.Contains(I)) {
SArr[I] = 'Y';
}
}
if (pK > 0) {
for (int I = 0; I <= UB; I++) {
if (ChangeIndSet1.Contains(I) == false) {
SArr[I] = 'X';
}
}
HashSet<int> ChangeIndSet2 = DeriveChangeIndSet(new string(SArr), YCnt - pK);
for (int I = 0; I <= UB; I++) {
if (ChangeIndSet2.Contains(I)) {
SArr[I] = 'Y';
}
}
}
// 解を求める
int Answer = 0;
for (int I = 0; I <= UB - 1; I++) {
if (SArr[I] == 'Y' && SArr[I + 1] == 'Y') {
Answer++;
}
}
return Answer;
}
// 文字列と変更回数を引数とし、XをYにするIndのSetを返す
static HashSet<int> DeriveChangeIndSet(string pStr, long pChangeCnt)
{
var ChangeIndSet = new HashSet<int>();
char[] CharArr = pStr.ToCharArray();
int UB = CharArr.GetUpperBound(0);
List<RunLen.RunLenInfo> RunLenInfoList = RunLen.DeriveRunLenInfoList(pStr);
var Tmp1 = RunLenInfoList.Where(pX => pX.AppearChar == 'X');
var Tmp2 = Tmp1.OrderBy(pX => pX.StaInd == 0);
var Tmp3 = Tmp2.ThenBy(pX => pX.EndInd == UB);
var Tmp4 = Tmp3.ThenBy(pX => pX.RunLen);
foreach (RunLen.RunLenInfo EachRunLenInfo in Tmp4) {
if (pChangeCnt == 0) break;
bool FromSta = true;
if (EachRunLenInfo.StaInd == 0) FromSta = false;
if (FromSta) {
for (int I = EachRunLenInfo.StaInd; I <= EachRunLenInfo.EndInd; I++) {
ChangeIndSet.Add(I);
if (--pChangeCnt == 0) break;
}
}
else {
for (int I = EachRunLenInfo.EndInd; EachRunLenInfo.StaInd <= I; I--) {
ChangeIndSet.Add(I);
if (--pChangeCnt == 0) break;
}
}
}
return ChangeIndSet;
}
}
#region RunLen
// ランレングス圧縮(文字列)
internal class RunLen
{
// ランレングス圧縮情報
internal class RunLenInfo
{
internal char AppearChar;
internal int RunLen;
internal int StaInd;
internal int EndInd;
}
// ランレングス圧縮結果を返す
internal static List<RunLenInfo> DeriveRunLenInfoList(string pBaseStr)
{
var WillReturn = new List<RunLenInfo>();
int StrUB = pBaseStr.Length - 1;
char PrevChar = pBaseStr[0];
int StrLen = 0;
for (int I = 0; I <= StrUB; I++) {
if (pBaseStr[I] != PrevChar) {
var WillAdd = new RunLenInfo();
WillAdd.AppearChar = PrevChar;
WillAdd.RunLen = StrLen;
WillAdd.StaInd = -1;
WillAdd.EndInd = -1;
WillReturn.Add(WillAdd);
StrLen = 0;
PrevChar = pBaseStr[I];
}
StrLen++;
if (I == StrUB) {
var WillAdd = new RunLenInfo();
WillAdd.AppearChar = pBaseStr[I];
WillAdd.RunLen = StrLen;
WillAdd.StaInd = -1;
WillAdd.EndInd = -1;
WillReturn.Add(WillAdd);
}
}
// StaIndとEndIndを設定
int RunSum = -1;
for (int I = 0; I <= WillReturn.Count - 1; I++) {
WillReturn[I].StaInd = RunSum + 1;
RunSum += WillReturn[I].RunLen;
WillReturn[I].EndInd = RunSum;
}
return WillReturn;
}
}
#endregion
解説
オセロセットで考察すると
YをXにするより、
XはYにするのが得だと分かります。
なので、YをXにすることを優先します。
端以外でなるべく短い塊を優先するのが最適です。
例えば、下記の場合は、2,5,6,0の優先順位になります。
01234567
XYXYYXXY
YをXしても、Kに残があった時の対応を考えます。
これは、反転候補を全てXにしてから、何個かYに変更できると考えれば、
実装量を減らすことができます。