

あなたは、とあるウェブサイトを管理していました。
paiza スキルチェック過去問題より
ある連続したk日間、このウェブサイトでキャンペーンを行ったのですが、いつからいつまでの期間に行ったかを忘れてしまいました。
幸い、ウェブサイトを運営していた全n日分のアクセスログが残っており、1日ごとの訪問者数が分かっています。
とりあえず、連続するk日の中で、1日あたりの平均訪問者数が最も多い期間を、キャンペーンを行った期間の候補だと考えることにしました。
n日分の訪問者数のリストとキャンペーンの日数kが入力されるので、キャンペーンを行った期間の候補数と、候補の中で最も早い開始日を出力してください。
この手の問題を考えるとき、頭に浮かぶ図がこれです。
入力例
5 3
1 2 3 2 1

ざっくりしたスプレッドシートで申し訳ありません。
範囲内に連続したかたまりがあり、それが1つずつ横にスライドしている。
スライドできなくなったらそこでおわり。
そういうイメージです。

<?php
// 自分の得意な言語で
// Let's チャレンジ!!
/*
n日分の訪問者数のリストとキャンペーンの日数kが入力されるので、
キャンペーンを行った期間の候補数と、候補の中で最も早い開始日を出力してください。
*/
$s = explode(" ",trim(fgets(STDIN)));
$num = $s[0];//全体の日数
$cam_num = $s[1];//キャンペーンの日数
$days = explode(" ",trim(fgets(STDIN)));//日ごとの訪問数
//print_r($day);
//キャンペーン期間の訪問者合計をだす
for ($i = 0; $i < $num-$cam_num+1; $i++) {
$sum[$i]=0;//reset 連続するキャンペーン日数の訪問数の合計
for ($j = 0, $k=$i; $j < $cam_num; $j++,$k++) {
$sum[$i]=$sum[$i]+$days[$k];
}
}
//print_r($sum);
$max = max($sum);
//echo($max);
$max_num = 0;//reset キャンペーンを行った期間の候補数
//最大値をもっている日程を探す
foreach($sum as $key=>$value){
if($value==$max){
$max_num++;
$answer[]=$key;
}
}
echo($max_num." ");
//print_r($answer);
$first_answer = $answer[0]+1;//候補の中で最も早い開始日/配列が0はじまりのため1を足す
echo($first_answer."\n");
?>
構造としては、
- 日にちの訪問者を配列に入れておく($days)
- キャンペーン日数(この場合3日)のかたまり【黄色部分】を1つずつ横にずらして取得したい
- よってforの始まりは$i = 0; ループ範囲は$i < $num-$cam_num+1;(全日程numからキャンペーン日数$cam_numを引き1を足す) $i++
- その合計値を配列に入れておく($sum) (なお$sumのキーはキャンペーン候補開始日にもなっています)
- $sumの最大値をもっているものが「もっともキャンペーンの3日というかたまりのなかで訪問者を取得している」ということになる
- 『キャンペーンを行った期間の候補数』が解答で求められているため、候補日は複数あると仮定し、最大値を持っているものの数を変数に格納($max_num)
- 同時に最大値をもっているもののキー(すなわち開始日候補)を$answerに格納
- 『候補の中で最も早い開始日を出力してください。』ということなので、$answer[0](配列のトップ)を出したいが、配列は0始まりで日にちは1始まりのため+1して出力
そういう流れになっています。

二重forで処理するのですが内側のforにはちょっと変数に仕掛けがいります。
$j =$i; でループを始めてしまうと「連続した3日」が、2回目のループの時には「連続した2日」、さらに「連続した1日」と目減りします。これはよくない。
よって$kをかませる必要がありました。
ご参考になれば幸いです。
paiza スキルチェック過去問題セット
https://paiza.jp/works/mondai/skillcheck_archive/problem_index?language_uid=php
3+
初コメ失礼します。
PHP学習中なのですが、
for ($j = 0, $k=$i; $j < $cam_num; $j++,$k++) {
$sum[$i]=$sum[$i]+$days[$k];
}
ここの部分ですが、なぜ$kを噛ませる事で、連続した三日を維持出来るのでしょうか?
結構、難しいなあと感じてしまいました。
あやぼうさんお疲れ様です。
for ($j = 0, $k=$i; $j < $cam_num; $j++,$k++) {
この部分ですが、最初、
for ($j=$i; $j < $cam_num; $j++) {
と記述したんですよ。
結果どうなったかというと、
「外側のfor文のカウント」が進み、$i=1、$i=2とすすむたび、
ちょうどこう記述した形になってしまったんです。
for ($j=2; $j < $cam_num; $j++) {
お気づきでしょうか?
こうなると$cam_numは定数の3ですので、
この内側のループは本来3日あるはずのキャンペーン期間を、
「2からはじめて3より小でおわる」1日分で済ませて終了しまいます。
内側のループは「キャンペーン期間」3日分でちゃんと回してほしい。
よって$jは「常に0から始めて」「3より小で」ループを終わらせる。
一方スライドする日付(開始日)も欲しい。それは$iに連動しています。
よって2つめの$kが登場し、$k=$i;となっています。
(そしてforループの終了条件は$jが持っています。$kは影響を受けません。 これによって3日が維持されます。)
もっとスマートで分かりやすい記述があるかもしれません。 私も万事勉強中ですのでご容赦ください。 ほんのご参考になれば幸いです!