トップページに戻る
次の競技プログラミングの問題へ
前の競技プログラミングの問題へ
No.38 赤青白ブロック
■■■問題■■■
赤と青と白のブロックが各10個ずつある。
ブロックは左から右に1列に並べられている。
並べるときには次のような決まりがある。
赤いブロックよりちょうどKr個左に赤いブロックがあってはならない。
赤いブロックよりちょうどKr個右に赤いブロックがあってはならない。
青いブロックよりちょうどKb個左に青いブロックがあってはならない。
青いブロックよりちょうどKb個右に青いブロックがあってはならない。
白いブロックについては特に制限は無い。
最初にKr、Kbの数値とブロックの並びが与えられる。
赤のブロックをR、青のブロックをB、白のブロックをWとする。
最初の状態は上記に示した条件を満たしていないかもしれない。
よって、操作として次のような操作が許される。
・赤か青のブロックをいくつか抜いて列の間を詰める。
例えば、「RWBRWWB」であれば、
左端のRだけ抜いて「WBRWWB」という列を作ることができる。
左から4つめのRと右端のBを抜いて「RWBWW」という列も作れる。
RとBはいくつでも選んで抜くことができる。
ただし、Wのブロックを抜くことはできない。
このような操作を行うことで条件を満たす最長の列を残したい。
どのようにRとBのブロックを抜くかは自由である。
残すことが可能な最長の列の長さはどれだけか?
■■■入力■■■
Kr Kb
S
1 <= Kr,Kb <= 29
SはRが10個、Bが10個、Wが10個からなる文字列
■■■出力■■■
残ったブロックの最大個数Ans個を表示せよ。
最後に改行を忘れずに。
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "Input1";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("1 10");
WillReturn.Add("RRRRRRRRRRWWWWWWWWWWBBBBBBBBBB");
//21
//Rの1個となりがRであってはならないのでRを9個抜く。
//Bの10個となりにBがあることは無いのでBは抜かなくてよい。
//最終的には RWWWWWWWWWWBBBBBBBBBB が残る。
}
else if (InputPattern == "Input2") {
WillReturn.Add("1 2");
WillReturn.Add("WRWWBBRRRWWBBRRRRWWWBBBRRWWBBB");
//22
}
else if (InputPattern == "Input3") {
WillReturn.Add("7 11");
WillReturn.Add("BWBWRWRRWBRRWRRWWBBBRRWBBRWBBW");
//27
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
List<string> InputList = GetInputList();
int[] wkArr = InputList[0].Split(' ').Select(X => int.Parse(X)).ToArray();
int Kr = wkArr[0];
int Kb = wkArr[1];
string S = InputList[1];
Console.WriteLine(ExecDFS(S, Kr, Kb));
}
struct JyoutaiDef
{
internal int CurrP;
internal string CurrStr;
internal int RemoveCnt;
}
//2の20乗通りの順列を列挙
static int ExecDFS(string pS, int pKr, int pKb)
{
var stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.CurrP = 0;
WillPush.CurrStr = "";
WillPush.RemoveCnt = 0;
stk.Push(WillPush);
int AnswerRemoveCnt = int.MaxValue;
while (stk.Count > 0) {
JyoutaiDef Popped = stk.Pop();
//クリア判定
if (Popped.CurrP > pS.Length - 1) {
if (IsOK(Popped.CurrStr, pKr, pKb)) {
if (AnswerRemoveCnt > Popped.RemoveCnt) {
AnswerRemoveCnt = Popped.RemoveCnt;
}
}
continue;
}
Action<string> PushSyori = pAddStr =>
{
WillPush.CurrP = Popped.CurrP + 1;
WillPush.CurrStr = Popped.CurrStr + pAddStr;
WillPush.RemoveCnt = Popped.RemoveCnt;
if (pAddStr.Length == 0) WillPush.RemoveCnt++;
//下限値枝切り
if (AnswerRemoveCnt <= WillPush.RemoveCnt)
return;
stk.Push(WillPush);
};
if (pS[Popped.CurrP] == 'B') {
PushSyori("B"); PushSyori("");
}
if (pS[Popped.CurrP] == 'R') {
PushSyori("R"); PushSyori("");
}
if (pS[Popped.CurrP] == 'W') {
PushSyori("W");
}
}
return pS.Length - AnswerRemoveCnt;
}
//OKなレンガの配置かを判定
static bool IsOK(string pS, int pKr, int pKb)
{
int UB = pS.Length - 1;
for (int I = 0; I <= UB; I++) {
if (pS[I] == 'R' && I + pKr <= UB && pS[I + pKr] == 'R')
return false;
if (pS[I] == 'B' && I + pKb <= UB && pS[I + pKb] == 'B')
return false;
}
return true;
}
}
解説
2の20乗通りの、除外するレンガの場所の順列を、全探索してます。