トップページに戻る
次の競技プログラミングの問題へ
前の競技プログラミングの問題へ
CODE FESTIVAL 2015予選A D 壊れた電車
■■■問題■■■
高橋鉄道では、N両編成の電車の一部が壊れてしまったため、
M人の整備士が点検をすることになりました。
i人目の整備士ははじめ、Xi両目の車両にいます。
それぞれの整備士は、今いる車両を点検することと、隣の車両に移動することができます。
車両の点検には時間はかかりませんが、隣の車両に移動するには1分かかります。
全ての車両を少なくとも1人の整備士が点検した状態になると点検作業は終了となります。
点検作業は最短何分で終了させることができるでしょうか。
■■■入力■■■
N M
X1
X2
・
・
・
XM
●1行目には、2つの整数 N(1 <= N <= 10億),M(1 <= M <= 10万,M <= N) が空白区切りで与えられる。
これは、電車がN両の車両からなり、整備士がM人いることを表す。
●2行目からのM行には、整備士の情報が与えられる。
このうち i(1 <= i <= M) 行目には、整数 Xi(1 <= Xi <= N) が与えられる。
これは、i人目の整備士がはじめ Xi 両目の車両にいることを表す。
ただし、Xiは全て相異なることが保証される。
また、整備士の情報は1両目の車両に近い順に与えられる、
つまり Xj<Xj+1(1 <= j <= M-1) であることが保証される。
■■■出力■■■
点検作業にかかる時間の最小値を分単位で1行に出力せよ。
出力の末尾に改行を入れること。
■■■サンプルケースのイメージ■■■
Input1 下の図のように整備士が移動すれば3分で点検作業を終了させることができます。
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("17 5");
WillReturn.Add("1");
WillReturn.Add("5");
WillReturn.Add("10");
WillReturn.Add("15");
WillReturn.Add("16");
//3
}
else if (InputPattern == "Input2") {
WillReturn.Add("66 10");
WillReturn.Add("8");
WillReturn.Add("9");
WillReturn.Add("16");
WillReturn.Add("23");
WillReturn.Add("37");
WillReturn.Add("47");
WillReturn.Add("51");
WillReturn.Add("52");
WillReturn.Add("53");
WillReturn.Add("64");
//8
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int N;
static int[] XArr;
static void Main()
{
List<string> InputList = GetInputList();
N = InputList[0].Split(' ').Select(X => int.Parse(X)).First();
XArr = InputList.Skip(1).Select(X => int.Parse(X)).ToArray();
Console.WriteLine(ExecNibunHou());
}
//2分法で最小の点検時間を返す
static int ExecNibunHou()
{
//0分で点検可能な場合
if (XArr.Length == N) return 0;
int L = 0;
int R = N * 2;
while (L + 1 < R) {
int Mid = (int)(((long)L + R) / 2);
if (CanTenken(Mid)) R = Mid;
else L = Mid;
//Console.WriteLine("現在のL={0},R={1}", L, R);
}
return R;
}
//K分で点検できるかを返す
static bool CanTenken(int pK)
{
int MitenkenMinNo = 1;
foreach (int EachX in XArr) {
if (MitenkenMinNo < EachX) {
//場合1 最小の未点検車両に到達不能の場合
if (MitenkenMinNo < EachX - pK)
return false;
//場合2 右に移動してから、最小の未点検車両に移動
int RestHun1 = pK;
RestHun1 -= EachX - MitenkenMinNo;
int MitenkenMinNoKouho1 = EachX + RestHun1 / 2 + 1;
//場合3 最小の未点検車両に移動してから、右に移動
int RestHun2 = pK;
RestHun2 -= (EachX - MitenkenMinNo) * 2;
int MitenkenMinNoKouho2 = EachX + Math.Max(0, RestHun2) + 1;
MitenkenMinNo = Math.Max(MitenkenMinNoKouho1, MitenkenMinNoKouho2);
}
//場合4 現在車両が点検済なら右に移動
else {
MitenkenMinNo = Math.Max(MitenkenMinNo, EachX + pK + 1);
}
//Console.WriteLine("最小の未点検車両={0}", MitenkenMinNo);
}
return N < MitenkenMinNo;
}
}
C++のソース
#include <stdio.h>
#include <valarray>
int ExecNibunHou(std::valarray<int> &pXArr);
bool CanTenken(int pK,std::valarray<int> &pXArr);
int GetMax(int p1,int p2);
int N;
int M;
int main()
{
scanf("%d %d",&N,&M);
std::valarray<int> XArr(M);
for(int I=1;I<=M;I++){
int wkInt;
scanf("%d",&wkInt);
XArr[I-1]=wkInt;
}
printf("%d\n",ExecNibunHou(XArr));
//for (int I = 0; I <= N * 2; I++) {
// if (CanTenken(I) == false) continue;
// printf("%d\n",I);
// break;
//}
}
//2分法で最小の点検時間を返す
int ExecNibunHou(std::valarray<int> &pXArr)
{
//0分で点検可能な場合
if ((int)pXArr.size()==N) return 0;
int L = 0;
int R = N * 2;
while (L + 1 < R) {
int Mid = ((long long)L + R) / 2;
if (CanTenken(Mid,pXArr)) R = Mid;
else L = Mid;
//printf("現在のL=%d,R=%d\n",L, R);
}
return R;
}
//K分で点検できるかを返す
bool CanTenken(int pK,std::valarray<int> &pXArr)
{
int MitenkenMinNo = 1;
for(int I=0;I<=(int)pXArr.size()-1;I++){
int EachX = pXArr[I];
if (MitenkenMinNo < EachX) {
//場合1 最小の未点検車両に到達不能の場合
if (MitenkenMinNo < EachX - pK)
return false;
//場合2 右に移動してから、最小の未点検車両に移動
int RestHun1 = pK;
RestHun1 -= EachX - MitenkenMinNo;
int MitenkenMinNoKouho1 = EachX + RestHun1 / 2 + 1;
//場合3 最小の未点検車両に移動してから、右に移動
int RestHun2 = pK;
RestHun2 -= (EachX - MitenkenMinNo) * 2;
int MitenkenMinNoKouho2 = EachX + GetMax(0, RestHun2) + 1;
MitenkenMinNo = GetMax(MitenkenMinNoKouho1, MitenkenMinNoKouho2);
}
//場合4 現在車両が点検済なら右に移動
else {
MitenkenMinNo =GetMax(MitenkenMinNo, EachX + pK + 1);
}
//printf("最小の未点検車両=%d\n",MitenkenMinNo);
}
return N < MitenkenMinNo;
}
int GetMax(int p1,int p2)
{
return (p1<p2 ? p2 : p1);
}
解説
左の車両から順番に点検していくようにして、
2分法で最小の分数を求めてます。