ABC103 C問題解けた

投稿者: | 2018年8月15日

今回解いた問題は、余りの合計の最大値を求める問題です。

[blogcard url=”https://beta.atcoder.jp/contests/abc103/tasks/abc103_c”]

わかってしまうと「なんだこれ、B問題レベルじゃねえか!」ってなる問題でした。

解法

最初考えたこと

全く頭が動かない状態で問題を読んでたので、とりあえず”愚直なコードを書いてmを求める”ということをしてました。以下がそのコードです。

はじめは、与えられる数列の最大値の余りが最大となるときのmのときだろって考えてたので、mを探すループ部分は以下の部分です。

[cpp] for(int i=1;i<100000*max;i++){ int sum=0; for(int j=0;j<n;j++){ sum+=i%a[j]; } if(sum>ans){ ans=sum; m=i; } } [/cpp]

sample2ぐらいならすぐ見つかると思ってたんですが全く答えと一致しなかったので、やけくそでループ回数増やしてます。アホですね。

ただ、やけくそでmを求めたおかげで、欲しいmの値は何でも存在するということに気がつけました。

真面目な解法

アホなことをしたお蔭で、欲しいmは必ず存在するということがわかりました。なので、もっとシンプルに考えます。

余りの合計が最大のときはどのような時かといえば、すべての余りが最大のときの合計値です。

例えば2なら余りの最大値は1、3なら2、10なら9・・・のように、その数の余りの最大値はその数自身より1小さい数です。

よって、求める解は、与えられた数列から1引いた数値の合計値となります。ということで、私の書いたコードは次のようになりました。

コンテスト中ならこういうのはさっさと解きたいものですが、そうじゃないのでまあ失敗するのもいいかなあって。

ただ、mを愚直に求めに言ったのは問題慣れしてないというか、なんというか・・・という・・・。精進します。

ABC094 C問題解けた

投稿者: | 2018年8月12日

いろいろとやってたら、気がつけばプログラムを書くという動作を1ヶ月ぐらいしてないことに気が付きました。

はっきり言ってマジでやべーです。配列が0スタートなのを忘れてバグ出しまくりました。

今回解いた問題は次の問題です。解法はバイト中に思いつきました。

[blogcard url=”https://beta.atcoder.jp/contests/abc094/tasks/arc095_a”]

中央値とは

私がまず躓いたのが「中央値って何だっけ?」ってことです。電流や電力と戯れてたら忘れました。

中央値とは

中央値(ちゅうおうち、英: median)とは、代表値の一つで、有限個のデータを小さい順に並べたとき中央に位置する値。たとえば5人の人がいるとき、その5人の年齢の中央値は3番目に年寄りな人の年齢である。ただし、データが偶数個の場合は、中央に近い2つの値の算術平均をとる。

wikipedia

sortした配列のど真ん中取ればいいということですね。

解法

まず元の配列をsortし、sortした配列の中央値を取ります。この時nは偶数ということが問題で保証されているので、中央値は2つ取ります。

次に元の配列を前から取り出し、先程の中央値と比較します。この時、比較する中央値は小さい方、大きい方どちらでもいいですが、それぞれ条件がちょっとだけ変わるので注意です。

最後に、中央値と元の配列の値を比較し、大きければ小さい方の中央値、小さければ大きい方の中央値を出力します。

以上のことをプログラムすると、次のようになります。

[cpp]

#include &lt;iostream&gt;
#include &lt;algorithm&gt;
using namespace std;
int main(int argc, char const *argv[]) {
int n;
cin&gt;&gt;n;
int a[n],b[n];
for(int i=0;i&lt;n;i++){
int tmp;
cin&gt;&gt;tmp;
a[i]=tmp;
b[i]=tmp;
}
sort(b,b+n);
// for(int i=0;i&lt;n;i++){
// cout&lt;&lt;b[i]&lt;&lt;",";
// }
// cout&lt;&lt;endl;
int med1=b[n/2-1],med2=b[n/2];
// cout&lt;&lt;med1&lt;&lt;","&lt;&lt;med2&lt;&lt;endl;
for(int i=0;i&lt;n;i++){
cout&lt;&lt;((a[i]&lt;med2)?med2:med1)&lt;&lt;endl;
}
return 0;
}

[/cpp]

バイト中にこれを思いついた時、自分のバカさ加減に呆れてました。「なんでこれをすぐに思いつけなかったのか・・・」と。

まず中央値ってなんだっけ?ってあたりからやばいです。ちゃんと勉強しよう・・・。

VANQUISHクリア

投稿者: | 2018年7月7日

tiwtter眺めてたら「VANQUISHってゲームが凄く面白いし、今steamセール中だから!!」っていうのがあったので、買って今一通りクリアしたので、その感想。

このゲームのウリのシステムはARモードらしい。スローモーションになっていろいろな行動ができるというやつです。よくわからずに「とりあえずスローモーションになったら弾避けたり敵撃ったりすればええんやろ?」と思ってたんですが、自分が投げた手榴弾を起爆できるらしい。そういった行動を繋げていける・・・らしいです。今人のレビュー読んで知りました。

続きを読む

Visual Studio Codeのメモ書きとか色々

投稿者: | 2018年6月28日

珍しくABC C問題が解けたから、調子に乗ってメモ書いた時、「Visual Studio CodeってWeb系書きやすいとか言ってたなあ」というのを思い出して、Visual Studio Codeをインストール。とりあえず使ってみました。

感想としては、「普通に使いやすくね・・・?」

私が最後にVisual Stuido Codeを触ったのが、出たばっかの時で、そのときは「Atomでええやん」となっていましたが、色々と便利になってから使ってみるととても使いやすいです。

  • ・軽いのでサクッと書くことができる
  • Emmetが標準で入ってる
  • 見やすい

AtomならEmmetはパッケージで入れればいいし、見やすさもカスタマイズすればいいんですけど、軽さだけはどうにもできない。早いのは正義ですね。

それはそうと、これは自分がよく使いそうな機能とかそういうのをメモしときます。

Visual Stuio Codeでの折返し

ショートカットキーはAlt+Zです。
[card url=”https://qiita.com/Ouvill/items/6fd08347602752acafe5″ title=”[Visual Studio Code]ウィンドウ幅の右端で行を折り返す。”]

ターミナルからVisual Stuido Codeを起動

shellをインストールすればいいです。

  1. Visual Stuido Code起動した状態でComand + SHift + Pでコマンドパレットを開く
  2. “Shell”と検索
  3. シェルコマンド:path内に”code”コマンドをインストールします、と選択

これで、ターミナルからVScodeが起動できるようになりました。
[card url=”https://qiita.com/naru0504/items/c2ed8869ffbf7682cf5c” title=”ターミナルからVisual Studio Codeを起動する方法【公式の方法】”]

HTMLファイルをプレビュー

プラグイン”Live HTML Previewer”をインストール。

インストール後再起動すれば終わり。

横にプレビューを表示するときは、コマンドパレット(Command+Shft+P)を開いて“show side preview”と入力して選択。いちいちCtrl+Sしなくていいから楽です。

以下はVS codeのことではなく、EmmetやMathjaxについてです。

Emmetでマークアップ

知ってたけど使ってなかった・・・めっちゃ便利やんけ!!

リスト作りたいときは”ul>li*3″と打てば要素数3のリストできるし、”h1+p”と打てば、見出しと段落でてくる。

詳しくは下のサイトがいいと思いいました。
[card url=”https://www.granfairs.com/blog/staff/efficiency-by-emmet-01″ title=”Emmetでマークアップを効率化しよう(HTML&カスタマイズ編)”]

Mathjaxで数式を書く

大学生なのにLaTeXが書けません。というか、レポートが手書きばっかで死にたくなります。

Mathjaxを使う

1,以下のコードをヘッダーに追加する

<script type=”text/javascript”
src=”https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-AMS-MML_HTMLorMML”>
</script>

2,プラグインをインストール(wordpress)
“Mathjax Latex”をインストールすればすぐに使えます。
どちらにせよ、Mathjaxを使うときは、式の両端に$$を付ける必要があります。
[card url=”https://bloglife.info/html-suusiki/” title=”HTMLで数式はどれだけ表せる?複雑な式を表示するには”]

インライン数式

文章中に数式を入れたい場合、インライン数式で本文の埋め込みます。
その時、 (…) で囲んで記述すれば、インライン数式として本文に埋め込めます。
[card url=”http://text.baldanders.info/remark/2017/10/getting-started-mathjax-3/” title=”ちょこっと MathJax: インライン数式と別行立て数式”]

ABC100 C解けた

投稿者: | 2018年6月28日

久しぶりにABCのC問題を真面目に解けたのでメモ。私のようなプログラム書けない馬鹿でもたま〜にちょっと整理すれば数学的に解ける問題あるから楽しい。私がまともに解けた問題は以下の問題。
[blogcard url=”https://beta.atcoder.jp/contests/abc100/tasks/abc100_c” title=”C – *3 or /2″]
問題を整理すれば、

    • “すべて”の数列の要素に対して
    • 2で割る
    • 3掛ける(ただし全て3を掛けてはいけない)

最初問題読んだ時、”すべて”というのを見落として、「これ無限に終わらなくね?」ってなりました。問題文はちゃんと読もう。
さて、ちゃんと問題を理解したところで、問題を解いていきましょう。

1,とりあえず偶数だけ考えれば良い

2と3は互いに素なので、3はいくらかけても2で割るという行為には影響しません。また、2で割れない奇数は3掛ける操作しかできない為、偶数のみ考えればいいということがわかります。
そこで最初に考えたことが、「2で割れる回数の最大回数じゃね?」ということですが、サンプル1でそれはおかしいことがわかります。しかし、2で割る回数という方針は正しかったです。
最初に考えた解法がコードを書いてサンプル1を通した瞬間に間違えてることがわかって落胆したので、次の解法を考えます。

2,とりあえず2で割ってしまう

「2で割れる回数」という方針は正しいはずです。でなければ、問題にがわざわざ「2で割る」という操作をさせるはずがありません。なので、この方針はそのままに真面目に考えました。
さて、「2で割る」ですが、どこまで「2で割る」のでしょうか。これは、「すべての要素に対して3倍できない」というところから考えられます。つまり、「すべての要素が2で割れなくなった」ところまで要素を割り続けます。
では、どのようにして「2で割り」ましょうか。求める解は「最大回数」ですので、2で割れる要素を同時に2で割ると、2で割るという操作が1回分回数が節約されます。これだと最大回数にならないので、考えるべきは「各要素を一個ずつ2で割っていく」という操作だとわかります。
また、複数の偶数に対して2で割れる最大回数を求める時、ある偶数を2で割り切れなくなるまで2で割り続け、2で割り切れなくなったら次の偶数を2で割り続けると考えればうまくいきます。また、2で割れきれなくなった偶数はその後考えませんが、思考の外では、これらは3を掛け続けている状態となっています。
まとめると、「各要素1個ずつ、その要素が2で割れなくなるまで2で割り続け、最終的にそのそれぞれの2で割った回数を足し合わせればいい」となります。
このあたりは、簡単な図をかいてみるとわかりやすかったです。
以上のことをプログラムで書くと次のようになりました。

[cpp]
        #include &amp;amp;amp;lt;iostream&amp;amp;amp;gt;
                using namespace std;
                int counter(int);
                int main(int argc, char const *argv[]) {
                  int n;
                  cin&gt;&gt;n;
                  int ans=0;
                  for(int i=0;i&amp;amp;amp;lt;n;i++){
                    int tmp;
                    cin&gt;&gt;tmp;
                    if(tmp%2==0)  ans+=counter(tmp);
                  }
                  cout&lt;&lt;ans&lt;&lt;endl;
                  return 0;
                }
                int counter(int a){
                  int count=0;
                  while(1){
                    if(a%2==0){
                      count++;
                      a=a/2;
                    }else{
                      break;
                    }
                  }
                  return  count;
                }
        [/cpp]

これを実行した結果、実行時間は7msでした。結果一覧を見てみると、こういう書き方をした場合7msのようです。しかし、実行時間2msの人もいるので、もっと高速化できるんだと思いました。
とは言っても、「2で割りまくる」というのは同じです。しかし、私がどうやって書いても2msにならなかったので諦めます。また、上のコードは無駄ばっかだったので、もっと短く書き直しました。

[cpp]

#include &lt;iostream&gt;
using namespace std;
int main(int argc, char const *argv[]) {
int n;
cin&gt;&gt;n;
int ans=0;
for(int i=0;i&lt;n;i++){
int a;
cin&gt;&gt;a;
while(a%2==0) a/=2,ans++;
}
cout&lt;&lt;ans&lt;&lt;endl;
return 0;
}

[/cpp]

最初からこれで書けばよかった・・・。

ボゴソートもどきの文字列作成

投稿者: | 2018年4月17日

「文字列をランダムで作ってPPAPになるコードでよくわからないエラーでるんだけど」と言われて、
エラー云々はよくわからなかったので、自分でそういうコードを書いてみました。

[cpp]
  #include &amp;lt;iostream&amp;gt;
  #include &amp;lt;time.h&amp;gt;
  #include &amp;lt;string&amp;gt;
  #include &amp;lt;random&amp;gt;
  #include &amp;lt;chrono&amp;gt;
  using namespace std;
  int dice(void);
  int main(int argc, char const *argv[]) {
    //A~Zのアルファベットの配列を作成
    char moji[26];
    int j=0;
    for(char i='A';i&amp;lt;='Z';i++){
      moji[j]=i;
      j++;
    }
    auto  start=chrono::system_clock::now();   //時間計測
    string s;
    int count=0;  //最悪∞回ループが回るので規定回数回ったら終了させたい用
    while(count&amp;lt;1000000){
      for(int i=0;i&amp;lt;4;i++){
        int j=dice();
        s.push_back(moji[j]);
      }
      if(s=="PPAP"){
        printf("%d回目にPPAPになりました。\n",count+1);
        break;
      }
      count++;
      s.clear();
    }
    if(count==1000000) cout&amp;lt;&amp;lt;"PPAPにはなりませんでした。"&amp;lt;&amp;lt;endl;
    //時間計測
    auto end=chrono::system_clock::now();
    auto dur=end-start;
    auto second=chrono::duration_cast&amp;lt;std::chrono::seconds&amp;gt;(dur).count();
    cout&amp;lt;&amp;lt;second&amp;lt;&amp;lt;endl;
    return 0;
  }
  //乱数発生関数
  int dice(void){
    random_device rnd;
    mt19937 mt(rnd());
    uniform_int_distribution&amp;lt;int&amp;gt; rand26(0,25);
    return rand26(mt);
  }
  [/cpp]

実行にかかる時間を測るためにchrono使っています。C++11で増えた機能全然知らなくてやばい。
[blogcard url=”https://cpprefjp.github.io/reference/chrono.html”]
アルファベットの配列を先に作ってます。もう少し綺麗に書けないかなあと。ただ、

[cpp]
  char moji[26]={'A','B',・・・,'Z'};
[/cpp]

って書くよりかはマシな気がします。まあ、先に作っておいて、コピペでいいけども。素数とかはそっちの方が早いですし。

実行時間は大体30秒程度でした。まあやってる話がボゴソートですし。