PKU2313

http://d.hatena.ne.jp/nuc/20060714#c1153070704

118Byte はこんな感じです。全然サクっとではなくて、死にものぐるいで色々やってます。なんかだいぶアルゴリズムが違う感じですね。というか nuc さんの回答見てて何やってるかわからない。一見して for(a;b;c)d; の d の部分を使ってないので 1Byte 削れそうに思います。

b,c,v;
main(a,m){
    for(gets(m);~scanf("%d",&a);b=a)
        v+=b?abs(m=b-a)-abs(b-(c=c&&m*b>m*c?m*a>m*c?a:c:b)):0;
    printf("%d",v);
}

でもとりあえず gets は削れるなーと思ったので修正して 117Byte 。まだ縮みそうな気はするんですけど、自分でも何やってるかあんまり思い出せなくて手が出ません。なんか他と比べてかなり自分的に好みの問題だったと思います。

b,c,v,m;
main(a){
    for(;~scanf("%d",&a);b=m++?a:0)
        v+=b?abs(m=b-a)-abs(b-(c=c&&m*b>m*c?m*a>m*c?a:c:b)):0;
    printf("%d",v);
}

これ、入力によってはたぶんコケると思います。最初は b, c を main の第二第三引数にして、 b>1e5 などとして、未入力であることを検知していたのですが、なんかこれで通ちゃったのでだいぶ削れてしまいました。ちなみに 120=>118 の 2Byte は改行の \n だったりします…

tekezoさんに鬼気迫る…と言っていただいたので、なんとなく説明とか書いてみます。

この問題は、 N, A(1), A(2), ..., A(N) が入力として与えられて、 B(N) を考えて、最小の V = |A(1)-B(1)|+|A(2)-B(2)|+...+|B(1)-B(2)|+|B(2)-B(3)|+... を求める問題。

普通なら A をとりあえずバッファに入れて…とか考えますが、既に nuc さんが 130Byte 代を出していたので、随時見ていく方法でなんとかいきそうだと思えます。

で、考えていくと、入力例として与えられている 3, 5, 8 だと、 A=B となる数列で最小の V が得られることがわかります。これは単純に横の要素と引き算して絶対値取るだけで大丈夫。

でも、 A = 3, 5, 4 のような入力のとき、 B = 3, 5, 4 だと V = 3 ですが、 B = 3, 4, 4 などとすると V = 2 にすることができます。というわけで、 B = A を基本として考えつつ、 3, 5, 4 や、 5, 2, 5 のように、曲線が凹凸になっている部分では、中央の値を近隣の値に補正するようにしています。

で、恒例なのですが、最初の 1 入力が N であって A(1) じゃないというのに気付かず血涙を出してから、 for 文の中身がだいたいこんな感じのコードになったのでした。

        v+=b<1e5?abs(a-b)
            -abs((c=c<1e5?
            b<a&&b<c?a<c?a:c
            :b>a&&b>c?a<c?c:a
                  :b:b)-b):0;

で、あとはまぁ、ひたすら削っただけ、なのですが、 ?a:c とか ?c:a とかを (b-a)*(b-c) の値なんかを使って一本化して、

        v+=b<1e5?abs(a-b)
            -abs(b-(c=c<1e5&&
                  (b-a)*(b-c)>0?(a-c)*(b-a)>0?a:c
                  :b)):0;

こんな感じ。あとは変数増やして m=a-b を先に代入しておくことにして、そしたら式変形が色々とできて今みたいな形になりました。あとは 1e5 が前述の通りなんかインチキぽく消えて、 \n を消して、 gets を消して、今に至ります。

削りたいのは、 c の代入文での () と、あと m のかけ算をしまくっている部分ももう少しなんか無いのかな?と思っています。アルゴリズム自体もなんかよくする方法はありげ。まぁそんなことやってる場合じゃないはずです。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h