みずまずぷろぐらみんぐ日記

日々学んだことや頭に浮かんだことを、そこはかとなく書き連ねます

【JavaScript】端っこseparatorでsplit

こんにちはー、みずまずです。

Cookieを読み込んであれこれしている時にsplit関数を使っていたのですが、個人的に新しい発見があったので書いていきます。



そもそもsplitメソッドとは…………

元の文字列を何かの目印(separator,区切り文字列のこと。コンマだったりスペースだったり文字や文字列だったり、自由に指定できる。)で切って、配列に入れてから渡してくれる関数ですね。

目印くんは配列には入らずおさらばします。



こんなような例はよく見るんじゃないでしょうか↓↓

var love = "coffee,ice cream,chocolate,neko";
//これをコンマで切って
var result1 = love.split(',');
//result1 == ["coffee", "ice cream", "chocolate", "neko"] こんな感じにしたり

var otaku = "Oshi is meccha toutoi";
//文章をスペースで切って
var result2 = otaku.split(' ');
//result2 == ["Oshi", "is", "meccha", "toutoi"] 単語毎にする

ただ上の2つの例はどちらも、分割する元の文字列の両端には目印が来ないんですよね。
じゃあこれはどうなるんでしょうか?

var answer = "Answer=Echigo-Seika";
result3 = answer.split("Answer=");
//result3 == [????????]


私はこの結果 ["Echigo-Seika"] こうなってると思ったんです。

なのでresult3[0]を使えば"Echigo-Seika"という文字列を取り出せると思ったんですが、結果は空文字列("")が返ってきてしまいました。



まさかと思いリファレンスを確認すると…………

separator が文字列の先頭または末尾、またはその両方に現れた場合、配列の先頭、末尾、または先頭と末尾の両方が、それぞれ空文字列になります。

ちゃんと書いてありました。


つまりさっきの例だとresult3の中身は

["", "Echigo-Seika"]

こんな配列になってるんですね。
何もなくても何もないのを取り出している…。


返ってきた配列の要素を取得する場合(ほとんどそうだと思いますが…)は、端っこで区切られてるかどうか考慮して添え字指定しなきゃだなぁと思いました。



リファレンス↓
developer.mozilla.org

httpサーバーに慣れてきました

こんばんはー、みずまずです。

今日も今日とてN予備の教材を進めてます。

Node.jsでサーバーを立てるのに少しずつ慣れてきたところです(ほんとか?)。

今日はこの前作った投票サイトに簡単な認証をつけたりもしました。

endメソッド

今までは
res.end();
みたいに引数なしでしか書いたことなかったんですが、中に文字入れたり関数入れたりできるんだね。


res.end('あひゃひゃひゃ');



res.write('あひゃひゃひゃ');
res.end();


みたいに動く。

関数を指定した場合は、関数実行して終了らしい。
知らなかったです。
endは一連のレスポンスが終わる時に必ず書くもの(勝手にendしてくれるpipe関数使った場合を除く)だと思ってました。

basic認証

basic認証を簡単に取り入れてみました。
(暗号化されてないので、流石にそのままじゃ使えないが。)


http.createServer(basic, (req, res) => {なんか処理とか…


のように第一引数にbasic入れるのが今までとは違ったので、そんな説明Node.jsのリファレンスのhttpのところに書いてあったか?と少々戸惑いました。
(実際書いてありません。)
どうやらhttp-auth側がbasic認証使う時はこうして書いてね!と指定したものらしく、パッケージの説明サイトにはちゃんと書かれていました。

www.npmjs.com

パッケージってこうやって使っていくんだなぁ。

realm

あと、basic認証の中でいろんなサイトとか飛び回って調べてもよくわからなかったのがrealmです。

"同一の認証を適用するひとまとまりの領域につけた名前"みたいな感じなんですかね。

名前(文字列)自体は自由につけられるっぽいので、そこまで気にしなくても良いものなのかな?

"realmに設定した値がどこかで表示される"って意見も見たのですが、見つけられませんでした。
うーん、何なんだ………。

Cookie

単語自体は今までもちらほら目にしたことがあったのですが(いろんなサイト見てると時々「有効にしてくれ~」とか「使うぞ~」とか言われるよね)、まぁ何か自分の端末にデータ保存したり、そこから引き出したりしてるんだろううな…くらいにしか思っていませんでした。

有効期限があるのは知らなかったです。


簡単な仕組み?みたいなものを、手を動かしながら勉強していました。

ヘッダーに載せる感じなんですね。



…………何かしまりのない感じになってしまいましたが、今回はこのくらいですかね。

明日からの1週間は、この続きでCookieもっと使えるようになったり、いつも通りABCの過去問解いたり、新しくReact触ったりしていきたいと思っています。


あ、そうだ!

昨日は初めてAtCoderで企業主催?のコンテストに出ました。
あんな感じなんですね~。

A,B,C問題までACできたので、順調に力はついてきてるんじゃないかと思います。

嬉しいな。


それじゃあ皆さん、おやすみなさい(´-ε-`).o0

Herokuを使ってみたよ

こんにちはー、みずまずです。

最近競プロが楽しくてN予備の学習が停滞していたので、今週はN予備校メインで進めています。
今回の記事はほぼその進捗日記のようなものです。


Hubotを使ったSlackのタスク管理ボットの開発も終わり、ここ数日はNode.jsでhttpサーバーを立ててアンケートをとる(サーバーにデータを送る)Webページを作っています。
HTTPのGETメソッドとPOSTメソッドの練習ですね。

同じようなフォーマットの複数のアンケートページを簡単に実装するために、テンプレートエンジン(Pug)というものも初めて使いました。
可愛い名前だなぁ…。


テキスト通りに進めれば実装&動作はすらすらいくのですが、細かい部分でたくさん疑問が湧いてしまい、それを消化するのに時間がかかった一週間でもありました。

こういうの、"これはこういうもの" "こういう書き方するの"で片付けられれば楽なんですが、気になると放っておけない&理屈なしで覚えられない性格なんですよね……。

数ヶ月後にはこんなん当たり前でしょ?と、もやもやしたことも忘れて使っているのかもしれませんが、後々自分が誰かに教える立場になれた時、かつて自分がどこでつっかかって時間を費やしたのか覚えていた方が、相手の立場に寄り添えるのかなと思うのでここに書き残しておきます。
(この記事完全に自分が振り返る用ですね。)


リクエストオブジェクトのurlにはホスト名が入らないの?

サーバー立てて最初に、GETでアクセスされたらアクセスしたURLを画面に返すだけのテストをしたのですが、ホスト名部分が入ってなくて疑問に思いました。
まぁ結果そういうものだ以外の何物でもないのですが、ファイルに追記したconsole.log(req);とリダイレクト( (実行するコマンド)>> (適当なファイル名) )でリクエストの中身をファイルに出力する方法を知れたり、リクエストの中身見れたりと発見は多かったです。
(この方法はN予備校のフォーラムで教えてもらったのですが、教えてくださった方によると正攻法はデバッガーを使うことらしいです。)
リクエストの中には想像よりずっといろんなデータが入っていて驚きました。

読み込み用のストリームをpipe関数を使ってレスポンスに流す時は.write()はいらないの?

今までレスポンスの内容はres.write('ほにゃらら~');みたいな感じで書いていたので結構困惑しました。
↑の必殺中身をログに出しちゃう法とかもやったのですが、余計わからなくなりました。
こちらもフォーラムで質問したんですが、レスポンスオブジェクト(公式リファレンス的にはhttp.ServerResponse)はストリーム形式であり、また.write()は引数に取ったものをストリームにして追加していくメソッドだよ、との回答を頂いて納得しました。
そりゃpipe関数で流すなら.write()出てきませんわ。

cloneした練習問題リポジトリのあるブランチに~~.jadeというファイルがあった。これ何?

別のブランチだとそれが~~.pugになっていたので何なんだろうと思ったら、jadeはPugの前の名前だったんですね。
拡張子まで変えちゃったんだ…当時不便なかったのかな………。まぁ今の方が名前かわいいしなぁ←

Heroku

アカウント登録をしました。
Herokuについては"AtCoder ScoresがHerokuで公開してたなー"くらいの知識しかありませんでした。
以前githubを使い始めた時に知ったgithub pagesじゃなく、わざわざこっちを使うのか?などとさえ思っていましたが、github pagesは静的なウェブサイト用のプラットフォームなんだね……。
無料で使うには制限があるとは言えHerokuしゅごい…。


N予備のテキストだけでデプロイまでできてしまったんですが、後になってアカウント登録後に出てきたチュートリア的なものを読んでみたら、凄く丁寧に書かれていてびっくりしました。
英語も読みやすく文章の量も少なく抑えらえていて、それでいて1つ1つの手順が省略されることなく説明されていました。gitコマンドとかコピペできるので初心者でもすぐ使えそう。
今日は最初の数ページ読んだだけですが、heroku logs --tailでリアルタイムにログを拾えるというN予備のテキストにない技も知れたので、時間のあるときに一度最後まで読んでみようと思います。

process.env.PORTとは?

いきなり出てきてなんやこれ~ってなったんですが、普通にprocessオブジェクトの中のenvというやつにいろんな環境変数が突っ込まれていて、その内のPORTってやつだよ、と書いてあげてるだけでした。
ちなみに実際はconst port = process.env.PORT || 8000;のように選択的代入を使って書いており、自分の環境ではPORTなんて何も設定してないので8000番ポートを使うことになり、console.log(process.env.PORT);の結果はundefinedでした。
Herokuとかで動かす時はHerokuくんが勝手にPORTにポート番号入れてくれるっぽいですね。
まぁそうじゃないとポート番号指定するとき困っちゃうか…。

snap(Snappy)って何?aptと何が違うの?自分で何か入れる時どっち使えばいいの?

これはHeroku CLIを入れる時にでてきました。sudo snap install ~~みたいな感じで。
調べたらそこそこ違いがありましたが、今回みたいに公式がsnap使ってる時はそれに従うようにして、それ以外は今のところaptで入れば基本そっちでいいかなと思いました。
そんなに古い環境使ってるわけじゃないしね(どこからが古いかと言われたらアレですが)…。


おまけ

日曜日のABC173でなんと初めてA,B,C,Dの4問を解くことができ(Dで2ペナ)"緑パフォ"というものを出しました!

みずまず 大☆成☆長

ここ2回のABCではコンテストになるとC問題がACできない状態に陥っていたのですが、こつこつ精進していて良かったです。

コンテスト終了残り7秒でD問題ACした時は心臓ばっくばくでした。
今後の人生でもそうそうないと思います……頭の中でThe Summer Wars流れてましたし(←もっと早く解け)


とは言えまだ茶色にはなれていませんし、"D解けたのまぐれだろ~"にならないように、これからも精進していきます。

AOJ登録、そしてscanfの罠にはまる…

どうも、みずまずです。


この記事に載ってる問題もだいたい解き終えて、残すところあと数問になりました。


以前取り上げたdrkenさんのtipsの記事の方で紹介されていたこちらの記事↓を最近読んだのですが

qiita.com


そう言えばAOJって聞いたことはあっても、使ったことなかったんですよね。
レッドコーダーさんがこんなに勧めているのだから、これはやるしかない…。


ということで、ここ1週間はABCの過去問と同時進行でAOJのコースにも取り組んでいました。

AOJに登録しました

AtCoderと微妙に仕様が違って出力の最後に改行必須だったりするので、チュートリアルHello Worldで2度もPresentation Errorになったりはしましたが、何だかんだで毎日4問くらいずつ進めて昨日、最初のコース(Introduction to Programming Ⅰ)が一通り終わりました。(※競プロとあまり関係ないらしい最後の4問除く。)


やったね!

これで茶コーダーへ一歩近づけてたら良いな←


実は取り組む前は

"APG4bで競プロ初心者に必要なc++の知識は学んだしABSも解いたしな~。やる意味あるでござるか~?"

となど思っていたのですが(おい)、結果やってみて良かったです。


このコース、問題によっては日本語表示にすると問題文の上下にnoteという欄があったりして、そこのヒントボタンや言語名が書かれたボタンを押すと説明に飛ぶんですね。
そこで必要な知識やどうやって解いていくかを教えてくれる。

最初の頃は"ヒントだとぉ?俺はそんなもの見ないぞ!!"という感じだったのですが、そこを読まないとほんとに問題解くだけになってしまうので、勉強し始めの人は先に見るのが嫌なら解いた後にでも目を通しておくと良いかもです。
APG4bではさらっと流されていたcの配列が普通に使われていたり、競プロの問題を解くときに便利な関数も紹介されていました。


それから問題ページの右上、提出アイコンの並びに吹き出しアイコンのDiscussion(AOJ Board?AOJ Thread?呼び方がいまいちわかりませんが)があって、問題について質問したり、議論したり、トラブルの報告もできるようになっています。
私は40問中1問だけどうしても詰まった問題があったのですが、質問を立てたところ親切な方から即行で回答をいただけてとても助かりました。

自分で考え、調べ、解決することは大切だと思いますが、ひとりでは抜け出せなくなった時に助けてもらえる仕組みがあるというのは凄く良いなと思いました。


そしてこれが私が感じたAtCoderとの1番の大きな違いなんですが、AOJの問題は1回の入力で複数のテストケースを突っ込んでくるものがまあまああります。
当然こちらでどこからどこまでが1まとまりのテストケースなのか、どこが入力の終わりなのか判断できるようにコードを書き、複数の結果を出力しなければいけません。

(もしかしたらAtCoderもARC、AGC、企業企画のコンテストとかだとそういうケースもあるのかもしれませんが、ABCのA~D問題しかやってこなかった私にとっては結構衝撃でした…。)
1つずつ入れてテストしてくれるABCはなんて優しくて親切な奴だったんだ………。
私は甘ちゃんだった…。


この仕組みのせいおかげで、while文をめちゃくちゃたくさん書きました。
あととにかく入出力の数が多いので、cin coutで>> <<を打つのがめんどくさすぎて、APG4bで習ったものの今まで1度も使わなかったscanf関数とprintf関数を使えるようになりました。
一生cin,coutだけで生きていけると思ってたのにな…。
案外scanf,printfの方が便利な時あるんですね。良い訓練になりました(*´ω`*)


ただこのscanfにはめらることになろうとは、この時のみずまずは思ってもいなかったのである。

Finding Missing Cards

先程"どうしても詰まった問題"があったと言いましたがそれがこれです。
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_6_B&lang=ja


俺の考えたコードは間違っていない!絶対にだ!

……という頑固おやじ風の自信と裏腹に何度やってもWA。


途中までは良いのに、何か出力が余分に出てくる。

ちなみにその時のコードがこれ。
強い人は、あー(ここね)wって思うんでしょうか…?

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  scanf("%d", &n);
  vector<vector<int>> v(4, vector<int>(14, 0));
  //key=>0=S,1=H,2=C,3=D value=>存在すれば1
  v.at(0).at(0) = 1;//数字を合わせるための調整
  v.at(1).at(0) = 1;
  v.at(2).at(0) = 1;
  v.at(3).at(0) = 1;
  for(int i = 0; i < n; i++) {
    char mark = ' ';
    int num = 0;
    scanf("%c %d", &mark, &num);
    if(mark == 'S') v.at(0).at(num) = 1;
    else if(mark == 'H') v.at(1).at(num) = 1;
    else if(mark == 'C') v.at(2).at(num) = 1;
    else v.at(3).at(num) = 1;//mark == 'D'
  }
  for(int i = 0; i < 4; i++) {
    char mark = (i == 0)? 'S'
      : (i == 1)? 'H'
        : (i == 2)? 'C'
          : 'D';
    for(int j = 0; j < 14; j++) {
      if(v.at(i).at(j) == 0) printf("%c %d\n", mark, j);
    }
  }
}


この問題は結局自分の力では直せなくてDiscussionで教えてもらい解決するのですが、scanf関数についてよく知らないまま使っていた私がまんまと罠にはまっていただけでした(チーン)

下のページ↓は回答してくれた方が載せてくださったものです。
rainbow.pc.uec.ac.jp

scanf関数では,正しく入力しても最後の改行は入力バッファに残るため,2度目以降の入力に%c指定をした場合,おかしな動作になります。


scanfでchar型を入れようとすると、改行を飛ばさず拾っちゃうんですね。
半角スペースとタブは飛ばしてくれるのに…(謎)


ということで、直すのはscanf("%c %d", &mark, &num);の部分ですね。
回避方法4を使って、scanf(" %c %d", &mark, &num);にしたところ無事AC。

たった半角スペース1つで上手く動くようになるなんて……。


あと知らなかったんですが、配列に入れる場合は'&'いらないんですね。
てかそもそも配列に一気に入れられるんですね。
おまけにAPG4bではscanfで直接文字列は入れられないって習った気がするんですが、文字列もOKなんですね(配列みたいなもんだしそういう感じ?)。


この問題のおかげでscanf関数やprintf関数について細かく知ることができましたし、よくわかっていないまま新しい構文や関数を使うのも危険だなと実感しました。(チャンチャン♪)


今回も最後まで読んでいただきありがとうございました。

はじめてのD問題AC!

こんにちはー、みずまずです。

気合を入れて臨んだ昨日のABC171ですが、とても悔しい結果に終わってしまいました。
A問題,B問題は過去最高ペースで通過したのに、Cで詰まり終了(チーン)

今日再度挑戦してみたのですがしたりしたのですが、方針まではコンテスト中に気づいていたのであっさりAC。


ぴえん。


ただコンテスト終了後にTwitter見てた感じだと、C飛ばしてD,Eを先に見に行った人結構いたみたいですね。


"単純にコードを書いたとしてもTLEになってしまう"
それがD辺りの問題の難しさなのかなと2度目のコンテスト(ABC170)参加時に感じ、アルゴリズムのアの字もわかっていない自分が手を出すには早いと思っていたのですが…………



CできなくてもDできた人がいるとわかると、やっぱりちょっと挑戦したくなるのもので……



今何となくやってみたらできました←


嬉しいです。

この記事で言いたかった事それだけです。

(とはいえ何も考えなかったわけではないです。
出てくる数字(特にループの回数を左右するやつ)の大きさを考えたり、むやみな多重ループは封印したり、四則演算の回数を少なくするにはどうしたらいいかななど、今の自分が持てる知識はちゃんと使いました。)


一般的にはC問題よりD問題の方が難易度が高いとは思いますが、今後はCに手を出して厳しいと思ったら"早い段階で"一度D問題も見てみるのもありかな、と思えました。


昨日はへこんだけど今日はちょっとるんるんなので、D問題の自分の回答を載せさせてください。
(おかしいところがあったらご指摘、ご指導、お願いします!)

D Replacing


制約から 1 ≤ N, Q, Ai, Bi, Ci ≤ 10^5 なので

・もしN=10^5でAiが全て同じ数だったら10^5個の要素の書き換えが発生するかも

・Q=10^5だったら操作は10^5回やらなきゃなので、その度にいちいち配列A(最大で10^5個の要素を持つ)の全要素を全部足し合わせて和Siを求めるのは時間超過しそう

・あと、存在しないkeyで配列[key]をしてアクセスしちゃうとValueの型の初期値が追加されるから、CはともかくBがAに存在しているか調べておかないとな
(配列.at(key)でのアクセスはエラーが出るので大丈夫(大丈夫かはわからんが追加はされない))

この辺りを意識して解きました。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n, q;
  cin >> n;
  map<int, int> a;//key=Ai,value=何回出てきたか
  int64_t s = 0;
  for (int i = 0; i < n; i++) {
    int ai;
    cin >> ai;
    if (a.count(ai)) a.at(ai)++;
    else a[ai] = 1;
    s += ai;
  }
  cin >> q;
  for (int i = 0; i < q; i++) {
    int b, c;
    cin >> b >> c;
    if (a.count(b)) {
      if (a.count(c)) {
        s += (c - b) * a[b];
        a[c] += a[b];
        a.erase(b);
      }else {//Aにcはない
        a[c] = a[b];
        a.erase(b);
        s += (c - b) * a[c];
      }
    }
    cout << s << endl;
  }
}


まず出現回数ごとに整数をまとめたいので、添え字を整数、値を出現回数とする配列を作ることも考えたのですが、要素数の大きな配列を使うのがこわいのでmapを使いました。

求めるものも和Siだけなので、最初の状態の和だけAiを入力する時に一緒に足し合わせておいて、Q回ある出力時にはBi→Ciの書き換えで生じる変化分だけ、簡単な足し引きでSの値を書き換えてあげるようにしました。

こんな感じですかね……。

なんか普段よりさらに需要がなさそうな記事になってしまった、な…。
APG4b育ちな書き方なので、APG4bでC++勉強中の方とかは回答例として読みやすいかも?そうであってくれ。


コンテストページ↓
atcoder.jp

AGC013 A Sorted Arraysとgoto 文とエラー

こんにちはー、みずまずです^^

AGCの過去問を解いていたら、goto 文使用時の注意点に気づいたので書いていきますね。


今回扱う問題はこちら↓
atcoder.jp


N=1,2(配列の要素が2個まで)ならどうしたって配列Aは単調非減少か単調非増加になるので、その場合はあれこれ処理するより先にgotoで最後の出力の文までワープできるようにしたくて、こんな感じのコードを書きました↓

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  cin >> n;
  int answer = 1;
  vector<int> a(n);
  if (n == 1 || n == 2) goto OWA;
  for (int i = 0; i < n; i++) cin >> a[i];
  int zogen = 0;//増加1→、減少-1→、初期値→0
  for (int i = 1; i < n; i++) {
    if(zogen == 1 && a[i-1] <= a[i]) continue;
    else if(zogen == 1 && a[i-1] > a[i]) {
      answer++;
      zogen = 0;
    }else if(zogen == -1 && a[i-1] < a[i]) {
      answer++;
      zogen = 0;
    }else if(zogen == -1 && a[i-1] >= a[i]) continue;
    else if(zogen == 0 && a[i-1] < a[i]) zogen = 1;
    else if(zogen == 0 && a[i-1] > a[i]) zogen = -1;
  }
  OWA:
  cout << answer;
}


が、これを実行するとエラーが……。

./Main.cpp: In function ‘int main()’:
./Main.cpp:24:3: error: jump to label ‘OWA’
   24 |   OWA:
      |   ^~~
./Main.cpp:9:30: note:   from here
    9 |   if (n == 1 || n == 2) goto OWA;
      |                              ^~~
./Main.cpp:11:7: note:   crosses initialization of ‘int zogen’
   11 |   int zogen = 0;//増加1→、減少-1→、初期値→0
      |       ^~~~~


goto 文関連のエラーっぽいですが、なぜかint型の変数zogen(変数のネーミングセンスは大目に見てください)も指摘されています。
"変数の初期化を横断している"みたいな感じですかね。

うーん、こんな時は
goto 文 - cppreference.com


説明欄とその1つ目の例がそれっぽい?

変数の宣言はできる(変数のスコープに入ることはできる)けど、初期化はできないよ
……ってことかな。

試しに以下のようにzogenの宣言の行をgotoより上に持ってきてあげたら無事通りました。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  cin >> n;
  int answer = 1;
  vector<int> a(n);
  int zogen = 0;//増加1→、減少-1→、初期値→0
  if (n == 1 || n == 2) goto OWA;
  for (int i = 0; i < n; i++) cin >> a[i];
  for (int i = 1; i < n; i++) {
    if(zogen == 1 && a[i-1] <= a[i]) continue;
    else if(zogen == 1 && a[i-1] > a[i]) {
      answer++;
      zogen = 0;
    }else if(zogen == -1 && a[i-1] < a[i]) {
      answer++;
      zogen = 0;
    }else if(zogen == -1 && a[i-1] >= a[i]) continue;
    else if(zogen == 0 && a[i-1] < a[i]) zogen = 1;
    else if(zogen == 0 && a[i-1] > a[i]) zogen = -1;
  }
  OWA:
  cout << answer;
}


ちなみに初期化が駄目以外にもVLA*1VM*2のスコープにはそもそも入ることもできないってことで、試しに配列の宣言" vector a(n); " を " goto OWA; " の次の行に移動させるのもやってみたんですが、やっぱり同じようなエラーが出てきました。
a(n, 0)みたいに初期値決めなくても「要素数nね!」って言うだけで駄目なんですね……。


goto 文、便利なんですけど、私みたいな初心者が使うには結構落とし穴が多いのかも?と思いました。

ちなみに前回goto 文使って落とし穴にはまった時の記事もあります。
良かったら読んでくださいな↓

mizumazu.hatenablog.com

*1:可変長配列(Variable Length Array, 長さを変えれる配列)のこと

*2:可変修飾 (variably modified; VM)のこと

競プロ的おはなし

こんにちはー、みずまずです。
2度目のABCコンテストで大成長を遂げた興奮から、日曜の夜はなかなか眠れませんでした(ぅω・`)

でも今回は運が良かっただけで、次また解けなくなっていたらすごくショックなので、パフォーマンスを落とさないように精進しなきゃ……と、翌日にはもうびくびくしていました。
(ネガティブ……この上なくネガティブ!!)

みずまず、目標決めたってよ

レートはとにかく上げたいです(プロフィールページのグラフビュンって右上がりにしたい)。
色もなるべく強い色になりたいです。
成長のスピードははやい方が理想です。
コンテスト後に強い人達やFFのみんながTwitterで議論してる内容を理解できるようになりたいし、私もその輪に入りたいです。

ただどうも具体的な精進の方法がぼんやりしていたので、"とにかく早く茶色になる"ということを短期的な目標として、それを達成するために何ができるようにならなくてはいけないのか、何をすべきか、を調べました。



"調べました"とは言いましたが
ABSの元となったdr.kenさんのQiitaの記事

qiita.com

とそのリンク記事

qiita.com

に必要なことはすべて書いてありました。ありがたや…。

tipsの方の記事によると茶色になるには

A~C の 3 問を早く解けるようにする (3 問合計で 30 分が目安です)


らしいです。

3問合計で…30分…………。

今の私は100分の制限時間内に確実にABC3問ACできるかも怪しい状況。
実は2回目の参加時に何分で問題をACできたか計っていたのですが、A,Bの2問解くのに30分、Cまで解き終わった頃にはコンテスト開始から75分が経っていました()


"""Cがどうこう以前にABの時点で遅い"""


ということで

・基本的にABCのB,Cレベルの問題だけ漁る。
・A,B(特にB問題)にかける時間短縮のために、B問題の過去問はスピードを意識して取り組む。
(たいていのA問題は問題見てからACするまでシンキングタイム0なので、とりあえずスルーします。タイピングが遅いとか、コードが冗長だとか、実行時間がーとか改善できるところはたくさんありますが、今はまだ細かいところを詰めるフェーズではないかなと。)
・C問題の過去問はなるべくいろんなパターンの問題に触れられるように選んで取り組み、コンテストで安定してACできるようにする。
(クセのある問題よりかは、典型的?なものを丁寧に解いていきたい。自力で解いたらいくつか他の人のコードも見てみて、より良いアプローチがあったか確認できたらいいな。)

しばらくはこんな感じで精進していこうと思います。
(C問題を短時間で解く的な訓練がないけど、それは次の段階かなって感じです…。)


方針、固まっちゃったね……///


周りの人達が強すぎて茶色ってすぐなれるものだと思ってたけど、意外と遠いんだなぁ、と思いました。競プロの世界は甘くなかった……。

ABC071 B Not Found

そんな感じで方針も決まったので、今週は先程リンクを貼った記事(1つ目の方)に載っている【類題】の問題を上からつぶしていました。
その中でひぃーーってなった問題について話します。

まず1つ目 ABC071 B Not Found。
集計処理というタイプの問題だそうですね。

APG4b育ちの私はset大好き?で、ABC085のB Kagami MochiやABC170のC Forbidden Listでもぱっと思いついたのはsetを使う解法だったのですが、せっかく配列を使ったバケット法なるものを学んだからには1度はやっておこう、ということで挑戦した問題です。
(B 問題辺りだとこのやり方でメモリが足りなくなることはほぼないらしいですね。良いことを知りました。)

#include <bits/stdc++.h>
using namespace std;

int main() {
  string s, answer = "None";
  cin >> s;
  vector<char> c(26, 0);
  for (int i = 0; i < s.size(); i++) c[(int)s.at(i) - (int)'a']++;//s[i]がaなら0,zなら25を増やす
  for (int i = 0; i < 26; i++) {
    if (c[i] == 0) {
      answer = (char)((int)'a' + i);
      break;
    }
  }
  cout << answer;
}

 
が、これsetの方が圧倒的に楽だったのでは…?()

整数とアルファベット対応させるのに少し苦労しました。
結果的にchar型とint型の変換にも慣れることができたので、そこは勉強になりましたが。
結構頑張りました…というおはなしです。

ABC074 C Sugar Water

全探索というタイプの問題らしいです。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int a, b, c, d, e, f;
  cin >> a >> b >> c >> d >> e >>f;
  vector<int> mizu;
  vector<int> sato(1,0);
  for (int i = 0; i <= f / 100 / a; i++) {
    for (int j = 0; j <= f / 100 / b; j++) {
      int ariemizu = (i * a + j * b) * 100;
      if (ariemizu != 0 && ariemizu <= f) mizu.push_back(ariemizu);
    }
  }
  for (int i = 0; i <= (f - 100 * a) / c; i++) {
    for (int j = 0; j <= (f - 100 * a) / d; j++) {
      int ariesato = i * c + j * d;
      if (100 * a + ariesato <= f) sato.push_back(ariesato);
    }
  }
  pair<int, int> answer(100 * a, 0);//first砂糖水,second溶けてる砂糖
  double maxnodo = 0.000000;
  double gendo = 100 * (double)e / (100 + (double)e);
  for (int i = 0; i < mizu.size(); i++) {
    for (int j = 0; j < sato.size(); j++) {
      int satomizu = mizu[i] + sato[j];
      double nodo = 100 * (double)sato[j] / (double)satomizu;
      if (satomizu <= f && maxnodo < nodo && nodo <= gendo) {
        maxnodo = nodo;
        answer = make_pair(satomizu, sato[j]);
      }
    }
  }
  cout << answer.first << ' ' << answer.second;
}

これははまりました。

まずどうやって解いたものか方針もなかなか決まらず、いざ書いてみたもののWA!WA!WA!
テストケース1個だけ通らなくてつらかったんですが、求める砂糖水の質量、砂糖の質量共に初期値を深く考えず0にしていて、解が初期値のまま1度も更新されないと無の砂糖水()が出来上がっていたんですね。問題文にわざわざ"砂糖が全く溶けていない水も濃度0 [%] の砂糖水と考えることにします"って書いてくれてあるし、制約でも"100A≦F"だって言ってるのに…。

あと、この問題と言いABC169のC Multiplication 3と言い、ほんとdouble型が信じられない…。
私の中でdouble型のイメージが"わりと頻繁に誤差出る上に、中で何が起こってるかわからないもの"になりつつあります。

正直こっちの問題はちゃんとわからずACしてる感ありますし。(よくないね。)

近々一度double型の安全な使い方を勉強しないとなーって思ってます。

あとがき

この他にTwitterで誰かが流してくれて気になった問題なんかにも興味が薄れないうちにと挑戦していたので、今週は1日平均9問くらいACしていました。
問題解くの、一度始めるとやめられなくなりますよね。

「もうこんな時間……寝なきゃ、明日起きれない………うーんでもあともう1問だけ←」

みたいな。
N予備とか他にもやることいっぱいあるのにね。こわいこわい。

明日のABC171、楽しみですが、先週のアレがまぐれだったと発覚してしまうんじゃないかとドキドキです。
頑張るぞい。