2025年3月10日月曜日

升目・盤面に関する問題(ABC201-250)

(ABC229-A, ABC250-A,ABC250-B)


特定の値を持つマスが辺で隣接しているか

ABC229-A

問題概要

2x2の格子が与えられる。その格子の中の全ての#が辺で隣接しているか。

解答

a=Array.new(2){Array.new(2)}
(0...2).each do |i|
    a[i]=gets.chomp.split("").to_a
end
ans="No"
n=0
(0...2).each do |i|
    (0...2).each do |j|
        if a[i][j]=="#" then
            n+=1
        end
    end
end
if n==3 or n==4 then
    ans="Yes"
end
if n==2 then
    if a[0][0]=="#" and a[1][1]=="#" then
        ans="No"
    elsif a[0][1]=="#" and a[1][0]=="#" then
        ans="No"
    else
        ans="Yes"
    end
end
puts ans    
    

上がコンテスト中に提出したコード。(2021-11-27 21:12:32)

Cで書いたのが以下。(2024-11-06 22:20:45)

#include<stdio.h>
#include<stdlib.h>
#define N1 4
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    char* s2=(char*)malloc(sizeof(char)*N1);
    if (s2==NULL){
        printf("memory allocation to sw failed.\n");
        exit(1);
    }
    char *ts2=fgets(s2,N1,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
    if(*s==35 && *s2==35 || *s==35 && *(s+1)==35 || *(s+1)==35 && *(s2+1)==35  || *s2==35 && *(s2+1)==35){
        printf("Yes\n");
        exit(0);
    }
    printf("No\n");
    return 0;
}
    

各マスが#か.かどうかで、4マスでは2^4=16通りのパターンが考えられるが、そのうち、全ての#が辺で隣接しているのは、((0,0),(0,1)), ((0,0),(1,0), ((1,0),(1,1)), ((0,1),(1,1)) が#の場合で、それが満たされていれば他のマスは何でもよい。すなわち、

## #? ?? ?#
?? #? ## ?#
の場合である。よって、この4通りの条件のいずれかが満たされているかどうかを調べればよい。

3年前にRubyで書いたときのコードを見てみたら、4隅のマスのうち対角線上の2ヶ所がともに.であれば条件を満たさないとしていた。確かにそう考えてもいい。同じ自分でも時が経つと違う発想をするものだと改めて思う。


辺で隣接する升目の数

ABC250-A

問題概要

h行w列の升目が与えらられる。マス(r,c)に辺で隣接するマスの個数を求めよ。

解答

コンテスト中にはまず以下のコードを書いたのだが、このコードでは15のテストケースのうち5つでWAになった。(2022-05-08 21:20:48)

h,w=gets.chomp.split(" ").to_a.map(&:to_i)
r,c=gets.chomp.split(" ").to_a.map(&:to_i)
ans=4
if (r==1 || r==h) && (c==1 || c==w)
    ans=2
elsif (r==1 || r==h) || (c==1 || c==w)
    ans=3
end
puts ans
    

このコードは、以下のように考えて書いた。

  • まず、原則として、1つのマスに辺で接する他の辺は上下左右の4つである。
  • 例外として、まず、マスが4つの隅のいずれかにあるとき、接するマスは2つになる。
  • また、マスが隅ではないが盤面の辺に接しているとき、接するマスは3つになる。

上のコードでは、盤面のサイズが3x3以上であることを前提としているが、制約ではh,wは1以上10以下であるから、縦または横の長さが1である場合も考慮しなければならない。それで書き直して提出したのが以下のコードだが、こちらもWAとなった。(2022-05-08 21:20:48)

h,w=gets.chomp.split(" ").to_a.map(&:to_i)
r,c=gets.chomp.split(" ").to_a.map(&:to_i)
ans=4
if h==1 && w==1
    ans=0
elsif (h==1) && (c==1 || c==w)
    ans=1
elsif h==1
    ans=2
elsif (w==1) && (r=1 || r=h)
    ans=1
elsif w==1
    ans=2
elsif (r==1 || r==h) && (c==1 || c==w)
    ans=2
elsif (r==1 || r==h) || (c==1 || c==w)
    ans=3
end
puts ans
    

このコードでは、以下のように考えている。

  • まず、盤面のサイズが1x1の場合、隣接するマスはないので、答えは0。
  • 盤面の縦または横の長さの一方のみが1であり、他方は1でない場合、(i) マス(r,c)が長さが1でない方向の端にあれば(具体的には、縦の長さが1であるときは左端か右端、横の長さが1であるときは上端か下端にあれば)、接しているマスは1個。(ⅱ)端になければ、接しているマスは左右または上下の2箇所。
  • それ以外の場合、最初に書いたコードと同じ。

    結果は、1つのテストケース(test_05.txt)でWAとなった。これでも結構試行錯誤した末に行きついたもので、A問題にしては難しいと感じた。結局、コンテスト中に正解することができなかった。

    今(2025年2月)改めて考えてみても、何が原因か分からない。しかも、間が悪いことに前年の暮れからAtCoderのテストケースが非公開となっており、もはやテストケースを見て原因を探ることができない。

    諦めかけたところでもう一度このコードをよく見てみると、二番目の条件式で、== とすべきところが = となっている。そこだけ直して提出したのが以下のコード。(2025-02-26 22:00:38)これでACになった。単純な書き間違いだったのに気付くのに3年弱かかってしまった。

    h,w=gets.chomp.split(" ").to_a.map(&:to_i)
    r,c=gets.chomp.split(" ").to_a.map(&:to_i)
    ans=4
    if h==1 && w==1
        ans=0
    elsif (h==1) && (c==1 || c==w)
        ans=1
    elsif h==1
        ans=2
    elsif (w==1) && (r==1 || r==h)
        ans=1
    elsif w==1
        ans=2
    elsif (r==1 || r==h) && (c==1 || c==w)
        ans=2
    elsif (r==1 || r==h) || (c==1 || c==w)
        ans=3
    end
    puts ans
        

    なお、解説を見ると、以下のような方法が示してあった。

    1. (r,c)が1行目でなければ、上のマスと接しているので、1を加算
    2. (r,c)がn行目でなければ、下のマスと接しているので、1を加算
    3. (r,c)が1列目でなければ、左のマスと接しているので、1を加算
    4. (r,c)がn列目でなければ、右のマスと接しているので、1を加算

    これでやってみたのが次のコード。(2025-02-26 22:24:03)

    h,w=gets.chomp.split(" ").to_a.map(&:to_i)
    r,c=gets.chomp.split(" ").to_a.map(&:to_i)
    ans=0
    if r!=1
        ans+=1
    end
    if r!=h
        ans+=1
    end
    if c!=1
        ans+=1
    end
    if c!=w
        ans+=1
    end
    puts ans
        

    Cで書いたときには、次のように考えて解いてみた。まず、与えられた盤面の大きさを一回り大きくした、(h+2)*(w+2)の大きさの盤面を作り、全てのマスの数字を0にする。続いて、盤面の一番外側のマスを除いたh*w個のマスの数字を1にする。そして、座標(r,c)の上下左右のマスの数字を足し合わせる。これで、もとの盤面上でマス(r,c)に隣接するマスの数が求められるはずである。それで書いたのが以下のコード。結果は、15件のテストケース中、4件のテストケースでREとなった。(2025-02-26 22:46:13)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int h=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int w=readint(s+i);
        free(s);
        char* s2=(char*)malloc(sizeof(char)*N1);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N1,stdin);
    //    printf("%s\n",s2);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int r=readint(s2);
        i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int c=readint(s2+i);
        free(s2);
    //    printf("h=%d w=%d r=%d c=%d\n",h,w,r,c);
        char ans=0;
        char* board=(char*)malloc(sizeof(char)*(h+2)*(r+2));
        int j;
        for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                *(board+(w+2)*i+j)=0;
            }
        }
        for(i=1;i<=h;i++){
            for(j=1;j<=w;j++){
                *(board+(w+2)*i+j)=1;
            }
        }
    /*    for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                printf("%d",*(board+(w+2)*i+j));
            }
            printf("\n");
        }*/
        ans+=*(board+(w+2)*(r-1)+(c));
        ans+=*(board+(w+2)*(r)+(c-1));
        ans+=*(board+(w+2)*(r)+(c+1));
        ans+=*(board+(w+2)*(r+1)+(c));
        printf("%d\n",ans);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    メモリの範囲外アクセスを疑うが、どこでそれが起きているのか分からない。サンプルのケースでは全てACになっている。テストケースの公開が停止されていることを恨むが、幸いというべきか、この問題では四つの変数がともに10以下なので、全ての組み合わせをしらみつぶしに試しても10^4通りだ。とりあえず、適当な数字を幾つか打ち込んで試してみる。

    予想していたよりも早く、h=10,w=10,r=1,c=1 とした時点で実行エラーになった。h=9,w=9 としても同じだ。

    ここで、原因は先の3年越しの解決となったrubyのコードと同じ、単純な書き間違いではないかと思い至る。そうして探してみると、見事に、wとすべきところをrとしている所が見つかった。

    そこを直して提出したのが以下のコード。これで全てACになった。(2025-03-03 21:21:25)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int h=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int w=readint(s+i);
        free(s);
        char* s2=(char*)malloc(sizeof(char)*N1);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N1,stdin);
    //    printf("%s\n",s2);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int r=readint(s2);
        i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int c=readint(s2+i);
        free(s2);
    //    printf("h=%d w=%d r=%d c=%d\n",h,w,r,c);
        char ans=0;
        char* board=(char*)malloc(sizeof(char)*(h+2)*(w+2));
        int j;
        for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                *(board+(w+2)*i+j)=0;
            }
        }
        for(i=1;i<=h;i++){
            for(j=1;j<=w;j++){
                *(board+(w+2)*i+j)=1;
            }
        }
    /*    for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                printf("%d",*(board+(w+2)*i+j));
            }
            printf("\n");
        }*/
        ans+=*(board+(w+2)*(r-1)+(c));
        ans+=*(board+(w+2)*(r)+(c-1));
        ans+=*(board+(w+2)*(r)+(c+1));
        ans+=*(board+(w+2)*(r+1)+(c));
        printf("%d\n",ans);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    長方形タイルの市松模様

    ABC250-B

    問題概要

    正方形を縦にa個、横にb個並べて一枚としたタイルがある。このタイルを縦横それぞれn枚並べる。各タイルは、#または.に塗られている。左上のタイルの色を.として、このタイル全体からなる模様を出力せよ。

    解答

    def toggle_flag(flag)
        if $flag==false
            $flag=true
        else
                $flag=false
        end
    end
    n,a,b=gets.chomp.split(" ").to_a.map(&:to_i)
    $flag=false
    n.times do |i|
        str=""
        n.times do |j|
            if $flag==true
                c="#"
            else
                c="."
            end
            str+=c*b
            toggle_flag($flag)
        end
        a.times do |k|
            puts str
        end
        if n%2==0
            toggle_flag($flag)
        end
    end            
        

    上がコンテスト中に提出したコード。よく見るとtoggle_flag関数の引数に意味がなかったが、まあいい。

    次に塗るタイルの色が黒か白かを表すフラグを用意する。フラグがtrueなら黒、falseなら白とする。右上のタイルは白なので、初めはフラグはfalseである。

    各行の出力用の空文字列を用意し、タイルの横幅分の#か.をまとめて連結。そして連結するごとにフラグを切り替える。こうして一行分の文字列が完成したら、それをタイルの縦の長さ分出力。

    続いて、次の行ブロックのタイルの出力に入りたいのだが、その前に、nの偶奇によって処理を分ける必要がある。すなわち、nが偶数であれば、ある行ブロックの最後の列のタイルの色と、次の行ブロックの最初の列のタイルの色は同じなのだが、奇数の場合には、異なる色となる。.なので、nが偶数の場合には、フラグの切り替えを行う。(最後の列のタイルを連結した後にいったんフラグを変えているので、もう一度フラグを切り替えることで、前の行ブロックの最後の列のタイルと同じ色にしている。)

    以上の処理をn回繰り返せばよい。

    ところで、市松模様では、行番号と列番号の偶奇が同じ場合と異なる場合で色が分かれる。つまり、(行番号、列番号)が(偶数、偶数)の場合と(奇数、奇数)の場合が一つの色となり、(偶数、奇数)の場合と(奇数、偶数)の場合が他方の色となる。これは、行番号+列番号が偶数になる場合(偶数+偶数、奇数+奇数は偶数)と奇数になる場合(偶数+奇数は奇数)とで色が異なると表現することもできる。

    だが、その性質を本問のように一文字ずつ市松模様にするのではない場合にどのように使ったらいいのか分からなかったので、コンテスト中には上記のような解法をとった。

    解説を見てみてみると、まず、タイルの色を格納したn*n行の行列Aを作り、それをi行j列を塗る際にA[i/a][j/b]の形で参照することで、その座標のタイルの色を得ている。一文字単位では縦がa*n行、横がb*n行なので、i,jをa,bで割った商が、ちょうどその場所の色を示す行列の行数と列数になるのだ。

    それでやってみたのが以下のコード。

    n,a,b=gets.chomp.split(" ").to_a.map(&:to_i)
    tiles=Array.new(n){Array.new(n,"")}
    n.times do |i|
        n.times do |j|
            if (i+j)%2==0
                tiles[i][j]="."
        else
                tiles[i][j]="#"
            end
        end
    end
    board=Array.new(n*a){Array.new(n*b,"")}
    (n*a).times do |i|
        (n*b).times do |j|
            board[i][j]=tiles[i/a][j/b]
        end
        puts board[i].join("")
    end
        

    Cで書いたとき、まずはrubyで最初に書いたときと同じように、b文字出力するごとに文字を切り替えるやり方でやってみた。それが以下のコード。(2025-03-05 21:29:25)結果は、18件のテストケース中5件でWAとなった。

    int readint(char *s);
    char color=46;
    void toggle(void){
        if(color==46)color=35;
        else if(color==35)color=46;
    }
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j,k,l;
        for(i=0;i<n;i++){
            for(j=0;j<a;j++){
                for(k=0;k<n;k++){
                    for(l=0;l<b;l++){
                        printf("%c",color);
                    }
                    toggle();
                }
                printf("\n");
            }
            if(n%2==0)toggle();
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    幸いにも、今回はサンプル中でも1件でWAとなっているので、そこを手掛かりにすることができる。そのテストケースでは、
    1 4 4 という入力例で、

    ....
    ....
    ....
    ....
    
    となるべき出力結果が
    ....
    ####
    ....
    ####
    
    となっている。

    何がいけなかったのかを考えてみると、上のコードはRubyで最初に書いたと基本的には同じやり方であるものの、一ヵ所違いがあることに気づく。Rubyで書いたコードでは、上の方法(b文字出力するごとに#と.を切り替える)で1行分の文字列を作った後、それをa回出力しているのに対し、今回Cで書いたコードでは、「b文字出力するごとに#と.を切り替えることをn回繰り返したのち、改行する」ことをa回繰り返しているのである。そのため、nが奇数のとき、本来ならば1つのタイル内では行の最後のマスと次の行の最初のマスが同じ色でなければならないのに、行の最後で色を切り替えたまま次の行に入ってしまい、次の行の最初のマスが別の色になってしまっているのである。

    そこを修正するために、一行の処理を終えた後、nが奇数ならばもう一度文字を切り替える処理を入れる。そして、これに伴って、Rubyで書いたコードでの、「nが偶数のとき、行方向にタイルが変わるごとに色を切り替える」処理の代わりに、「行方向にタイルが変わるごとに、常に色を切り替える」処理をすることになる。なぜなら、行の最後のb文字の出力を終えた後に一度文字の切り替えを行い、nが奇数のときだけ各行ごとに文字の切り替えを行い、タイルが変わる行に入る時にまた文字を切り替えることで、タイル行が変わる際、nが偶数ならば2回、nが奇数ならば3回の文字の文字の切り替えが行われることになる。であるから結局、nが偶数のときには、2回の切り替えが行われた結果色は変わらないことになり、新しいタイル行の最初のマスは前のタイル行の最後のマスと同じ色になるからである。

    それが以下のコード。(2025-03-05 21:49:41 )

    int readint(char *s);
    char color=46;
    void toggle(void){
        if(color==46)color=35;
        else if(color==35)color=46;
    }
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j,k,l;
        for(i=0;i<n;i++){
            for(j=0;j<a;j++){
                for(k=0;k<n;k++){
                    for(l=0;l<b;l++){
                        printf("%c",color);
                    }
                    toggle();
                }
                if(n%2==1)toggle();
                printf("\n");
            }
            toggle();
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    Cで書いたもう一つのコードは、コンテスト中にRubyで書いたときには考えながらも実装できなかった、「市松模様では、「行番号+列番号」の偶奇と色が対応している」という性質を使ったもの。これに、a*n行、b*n列の行、列の番号をそれぞれ a,b で割ったものが、各タイルを1つのマスとしたときの行番号、列番号になる」ということを組み合わせると、以下のようなコードになる。(2025-03-05 22:09:42)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j;
        for(i=0;i<n*a;i++){
            for(j=0;j<n*b;j++){
                if((i/a+j/b)%2==0)printf("%c",46);
                else printf("%c",35);
            }
            printf("\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    2025年3月5日水曜日

    文字列に関する問題(ABC201-ABC250)

    (ABC202-B, ABC211-B, ABC215-A, ABC217-A, ABC217-B, ABC218-A, ABC219-B, ABC221-B, ABC223-B, ABC224-A, ABC230-B, ABC232-B, ABC233-B, ABC236-A, ABC242-B, ABC244-A, ABC248-A)


    逆順への並び替えと置換

    ABC202-B

    問題概要

    0,1,6,8,9からなる文字列が与えられる。この文字列を逆順に並べたうえ、6は9に、9は6に変換して出力せよ。

    解答

    s=gets.chomp
    n=s.size
    n.times do |i|
        c=s[n-1-i]
        if c=="6"
            c="9"
        elsif c=="9"
            c="6"
        end
        print c
    end
    print "\n"
        

    この時は本番不参加。上のrubyのコード(2024-04-12 22:58:53)と下のCのコード(2024-04-12 23:09:05 )は同時に書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1e5+2
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s1+i)>=48 && *(s1+i)<=57)i++;
        i--;
        while(i>=0){
            char c=*(s1+i);
            if(c==54)c=57;
            else if(c==57)c=54;
            printf("%c",c);
            i--;
        }
        printf("\n");
        return 0;
    }
        

    文字列のリストが一致するか。

    ABC211-B

    問題概要

    4つの文字列が与えられる。これが、H,2B,3B,HR それぞれ1つずつか判定せよ。

    解答

    #n=gets.chomp.to_i
    s=[]
    (0..3).each do |i|
        s[i]=gets.chomp
    end
    #n,l=gets.chomp.split(" ").map(&:to_i)
    ans=""
    s.sort!
    str=["2B","3B","H","HR"]
    
    if s==str then
        ans="Yes"
    else
        ans="No"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-24 21:12:33)

    Cで書いたコードが以下。(2024-05-22 23:19:17)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 4
    int main(void){
        int *ref=(int*)malloc(sizeof(int)*4);
        for(int i=0;i<4;i++)*(ref+i)=0;
        for(int i=0;i<4;i++){
            char* s1=(char*)malloc(sizeof(char)*N1);
            if (s1==NULL){
                printf("memory allocation to s1 failed.\n");
                exit(1);
            }
            char *ts1=fgets(s1,N1,stdin);
            if(ts1==NULL){
                printf("fgets(s1) failed\n");
                exit(1);
            }
    //        printf("%s",s1);
            if(*s1==50 && *(s1+1)==66)*(ref+1)=1;
            else if(*s1==51 && *(s1+1)==66)*(ref+2)=1;
            else if(*s1==72 && *(s1+1)==82)*(ref+3)=1;
            else if(*s1==72)*ref=1;
        }
    //    for(int i=0;i<4;i++)printf("%d ",*(ref+i));
        int flag=1;
        for(int i=0;i<4;i++){
            if(*(ref+i)==0){
                flag=0;
                break;
            }
        }
    //    printf("flag=%d\n",flag);
        if(flag==1){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
        

    rubyで書いたときは、与えられた四つの文字列からなる配列をソートし、それが 2B,3B,H,HRという配列と一致するかどうかで判定したが、Cで書いたときには出現すべき文字列が全て出てきているかで判定している。


    文字列の一致判定

    ABC215-A

    問題概要

    与えられた文字列が Hello,World! と完全に一致するか。

    解答

    #n=gets.chomp.to_i
    s=gets.chomp
    ans="WA"
    if s.eql?("Hello,World!") then
        ans="AC"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-08-21 21:04:08)

    Cで書いたのが以下。(2024-06-12 22:59:37)

    int slen2(const char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 17
    #define N2 12
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        char hello[]="Hello,World!";
        int size=slen2(s1);
    /*    printf("s=%s\n",s1);
        printf("size of s=%d\n",size);
        printf("hello=%s\n",hello);
        printf("size of hello=%d\n",slen2(hello));*/
        if(size!=N2){
            printf("WA\n");
            exit(0);
        }
        for(int i=0;i<N2;i++){
            if(*(s1+i)!=*(hello+i)){
                printf("WA\n");
                exit(0);
            }
        }
        printf("AC\n");
        return 0;
    }
    int slen2(const char *s){
        int i=0;
        while((*(s+i)>=97 && *(s+i)<=122) || (*(s+i)>=65 && *(s+i)<=90) || *(s+i)==44 || *(s+i)==33)i++;
        return i;
    }
        


    文字列の辞書順比較

    ABC217-A

    問題概要

    文字列sは文字列tよりも辞書順で前に来るか。

    解答

    s,t=gets.chomp.split(" ")
    ans="No"
    if s<t then
        ans="Yes"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-04 21:07:25)

    Cで書いたのが以下。(2024-10-04 22:59:07)

    int scmp(char* s, char* t);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 23
    #define N2 11
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s1+i)>=97 && *(s1+i)<=122){
            i++;
        }
        *(s1+i)=0;
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i++;
        int j=0;
        while(*(s1+i)>=97 && *(s1+i)<=122){
            *(s2+j)=*(s1+i);
            i++; j++;
        }
        *(s2+j)=0;
    //    printf("s1=%s s2=%s\n",s1,s2);
        if(scmp(s1,s2)<0){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
    int scmp(char* s, char* t){
        int i=0;
        while(1){
            if (*(s+i)==0 && *(t+i)==0) return 0;
            else if (*(t+i)!=*(s+i)) return (*(s+i)-*(t+i));
            else i++;
        }
    }
        

    含まれていない文字列

    ABC217-B

    問題概要

    3つの文字列が与えられる。ABC,ARC,AGC,AHCのうち、与えられた文字列にないものを出力せよ。

    解答

    a=["ABC","ARC","AGC","AHC"]
    b=[]
    3.times do
        t=gets.chomp
        b.push(t)
    end
    puts (a-b)[0]
        

    上がコンテスト中に提出したコード。(2021-09-04 21:11:56)

    Cで書いたのが以下。(2024-10-04 23:23:38)

            int scmp(char* s, char* t);
            #include<stdio.h>
            #include<stdlib.h>
            #define N1 5
            int main(void){
                char* s1=(char*)malloc(sizeof(char)*N1);
                if (s1==NULL){
                    printf("memory allocation to s1 failed.\n");
                    exit(1);
                }
                char *ts1=fgets(s1,N1,stdin);
                if(ts1==NULL){
                    printf("fgets(s1) failed\n");
                    exit(1);
                }
                int i=0;
                while(*(s1+i)>=65 && *(s1+i)<=90){
                    i++;
                }
                *(s1+i)=0;
                char* s2=(char*)malloc(sizeof(char)*N1);
                if (s2==NULL){
                    printf("memory allocation to s2 failed.\n");
                    exit(1);
                }
                char *ts2=fgets(s2,N1,stdin);
                if(ts2==NULL){
                    printf("fgets(s2) failed\n");
                    exit(1);
                }
                i=0;
                while(*(s2+i)>=65 && *(s2+i)<=90){
                    i++;
                }
                *(s2+i)=0;
                char* s3=(char*)malloc(sizeof(char)*N1);
                if (s3==NULL){
                    printf("memory allocation to s3 failed.\n");
                    exit(1);
                }
                char *ts3=fgets(s3,N1,stdin);
                if(ts3==NULL){
                    printf("fgets(s3) failed\n");
                    exit(1);
                }
                i=0;
                while(*(s3+i)>=65 && *(s3+i)<=90){
                    i++;
                }
                *(s3+i)=0;
                char* abc="ABC";
                char* arc="ARC";
                char* agc="AGC";
                char* ahc="AHC";
                int a[4]={0};
                if(scmp(s1,abc)==0 || scmp(s2,abc)==0 || scmp(s3,abc)==0){
                    a[0]=1;
                }
                if(scmp(s1,arc)==0 || scmp(s2,arc)==0 || scmp(s3,arc)==0){
                    a[1]=1;
                }
                if(scmp(s1,agc)==0 || scmp(s2,agc)==0 || scmp(s3,agc)==0){
                    a[2]=1;
                }
                if(scmp(s1,ahc)==0 || scmp(s2,ahc)==0 || scmp(s3,ahc)==0){
                    a[3]=1;
                }
            /*    printf("s1=%s s2=%s s3=%s\n",s1,s2,s3);
                for(int i=0;i<4;i++){
                    printf("%d ",*(a+i));
                }*/
                if(a[0]==0){
                    printf("ABC\n");
                }else if(a[1]==0){
                    printf("ARC\n");
                }else if(a[2]==0){
                    printf("AGC\n");
                }else if(a[3]==0){
                    printf("AHC\n");
                }
                return 0;
            }
            int scmp(char* s, char* t){
                int i=0;
                while(1){
                    if (*(s+i)==0 && *(t+i)==0) return 0;
                    else if (*(t+i)!=*(s+i)) return (*(s+i)-*(t+i));
                    else i++;
                }
            }
            
        

    文字列のi番目の文字

    ABC218-A

    問題概要

    oとxとからなる文字列と整数nが与えられる。文字列のn番目の文字はoか。

    解答

    n=gets.chomp.to_i
    s=gets.chomp.split("").to_a
    ans="No"
    if s[n-1]=="o" then
        ans="Yes"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-11 21:05:58)

    Cで書いたのが以下。(2024-10-07 22:24:56)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 3
    #define N2 9
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int n=readint(s1);
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        if(*(s2+n-1)==111){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    文字列の連結

    ABC219-B

    問題概要

    3つの文字列s[1],s[2],s[3]と、1,2,3を要素とする整数列{a_n}が与えられる。整数列の順番にs[a[i]]を連結した文字列を出力せよ。

    解答

    s1=gets.chomp
    s2=gets.chomp
    s3=gets.chomp
    t=gets.chomp.split("").map(&:to_i)
    n=t.size
    ans=""
    n.times do |i|
        if t[i]==1 then
            ans+=s1
        elsif t[i]==2 then
            ans+=s2
        elsif t[i]==3 then
            ans+=s3
        end
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-18 21:13:37)

    Cで書いたのが以下。(2024-10-09 23:04:12)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 3
    #define N2 10
    #define N3 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*((N2+1)*N1));
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        int idx=0;
        for(int i=0;i<N1;i++){
            char* s1=(char*)malloc(sizeof(char)*(N2+2));
            if (s1==NULL){
                printf("memory allocation to s1 failed.\n");
                exit(1);
            }
            char *ts1=fgets(s1,N2+2,stdin);
            if(ts1==NULL){
                printf("fgets(s1) failed\n");
                exit(1);
            }
    //        printf("s1=%s",s1);
            idx=(N2+1)*i;
            int j=0;
            while(*(s1+j)>=97 && *(s1+j)<=122){
                *(s+idx+j)=*(s1+j); j++;
            }
    //        *(s+idx+j)=0;printf("j=%d\n",j);
            free(s1);
        }
    /*    for(int i=0;i<3;i++){
            printf("%s\n",s+i*(N2+1));
        }*/
        char* s3=(char*)malloc(sizeof(char)*N3);
        if (s3==NULL){
            printf("memory allocation to s3 failed.\n");
            exit(1);
        }
        char *ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(s3) failed\n");
            exit(1);
        }
        int k=0;
        while(*(s3+k)>=49 && *(s3+k)<=51){
            printf("%s",(s+(*(s3+k)-49)*11)); k++;
        }
        printf("\n");
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC221-B

    問題概要

    文字列sの隣り合う2文字を選び入れ替えることを高々1回行うことで、sをtと一致させることができるか。

    解答

    s=gets.chomp.split("").to_a
    t=gets.chomp.split("").to_a
    a=t.size
    #b=(0...a).to_a.combination(2).to_a
    #sz=a*(a-1)/2
    def swap(x,y)
        x,y=y,x
    end
    if s==t then
        puts "Yes"
        exit
    end
    (0...a-1).each do |i|
        #t[b[i][0]],t[b[i][1]]=t[b[i][1]],t[b[i][0]]
        t[i],t[i+1]=t[i+1],t[i]
        if s==t then
            puts "Yes"
            exit
        else
            #t[b[i][0]],t[b[i][1]]=t[b[i][1]],t[b[i][0]]
            t[i],t[i+1]=t[i+1],t[i]
        end
    end
    puts "No"
        

    上がコンテスト中に提出したコード。(2021-10-02 21:39:41)

    Cで書いたのが以下。(2024-10-16 22:53:12)

    int scmp1(char* s, char* t, int n);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 102
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* t=(char*)malloc(sizeof(char)*N1);
        if (t==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(t,N1,stdin);
        if(ts2==NULL){
            printf("fgets(t) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122){
            i++;
        }
        *(s+i)=0;
        *(t+i)=0;
        int n=i;
        if(scmp1(s,t,n)==0){
            printf("Yes\n");
            exit(0);
        }
        i=1;
        for(;i<n;i++){
            char temp=*(s+i-1);
            *(s+i-1)=*(s+i);
            *(s+i)=temp;
            if(scmp1(s,t,n)==0){
                printf("Yes\n");
                exit(0);
            }
            *(s+i)=*(s+i-1);
            *(s+i-1)=temp;
            
        }
        printf("No\n");
        return 0;
    }
    int scmp1(char* s, char* t, int n){
        int i=0;
        while(1){
            if (*(s+i)==0 && *(t+i)==0) return 0;
            else if (*(t+i)!=*(s+i)) return (*(t+i)-*(s+i));
            else i++;
            if(i>n){
                printf("s=%s, t=%s, i=%d\n",s,t,i);
                printf("Comparing failed.\n");
            }
        }
    }
        

    辞書順で最大と最小

    ABC223-B

    問題概要

    与えられた文字列に対し、「先頭の文字を末尾に移動する操作」ないしは「末尾の文字を先頭に移動する操作」を任意の回数行って得られる文字列のうち、辞書順で最小のものと最大のものを求めよ。

    解答

    #n,p=gets.chomp.split(" ").map(&:to_i)
    #a=gets.chomp.split(" ").map(&:to_i)
    s=gets.chomp
    maxs=s
    mins=s
    sa=s.split("").to_a
    sz=s.size
    t=[]
    ts=""
    (0...sz).each do |i|
        temp=t[sz-1]
        (0...sz).each do |j|
            if j-i>=0 then
                t[j]=sa[j-i]
            else
                t[j]=sa[j+sz-i]
            end
        end
        ts=t.join("")
        if ts>maxs then
            maxs=ts
        end
        if ts<mins then
            mins=ts
        end
    end
    puts mins
    puts maxs
        

    上がコンテスト中に提出したコード。(2021-10-17 21:26:51)

    Cで書いたコードについて、まず誤ったコードを以下に示す。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 6
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
    //    printf("s=%s\n",s);
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
    //    printf("sd=%s\n",sd);
        char min=122, max=97;
        int min_pos=0, max_pos=0;
        for(i=0;i<sz;i++){
            if(*(s+i)<min){
                min=*(s+i);
                min_pos=i;
            }
            if(*(s+i)>max){
                max=*(s+i);
                max_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+(i%sz)));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+(i%sz)));
            }
        printf("\n");
        return 0;
    }
        

    右シフトを行っても左シフトを行っても、得られるのは元の文字列から作った環状配列の一部である。だから、s[min]の場合には最初の文字が最も辞書順で小さいものとなるように環状配列の開始位置を選べばよいのではないかと考えて書いたのがこのコードである。しかし、このアルゴリズムでは、例えばaabaとaaabの大小を正しく把握できず、最初に見つかった前者を誤って最小の文字列としてしまうことが分かった。

    とすると、やはり文字列全体を見て大小を比較するしかない。しかし、毎回文字列の大小比較をするのは効率が悪いように思われるし、文字列の大小比較用に作った自作関数のscmpは文字列の最後にヌル文字があることを前提に作っている。(これは、組み込みのstrcmp関数でも同じはず。)そこで、今回は、文字列を26進数の数値とみなし、それを10進法に直した数値どうしで大小を比較することにした。文字列どうしを直接比較する場合、早ければ最初の文字どうしで大小が決まってしまうのに対し、この変換を行う場合には文字列を最後まで読まなければならないから、今回の問題に関してはこちらの方が計算量が多くなってしまうかの感はあるのだが、一度数値に直してしまえばあとは整数型どうしの比較で済むので、同じ文字列を何回も比較にかけなければならないソートなどの場合にはこちらの方が計算量を少なくできるのではないか。そう思って書いたのが以下。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 6
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
        int min=456975, max=0;
        int min_pos=0, max_pos=0;
        for(int i=0;i<sz;i++){
            int k=0;
            for(int j=0;j<sz;j++){
                k*=26;
                k+=*(sd+i+j)-97;
            }
            if(k>max){
                max=k;
                max_pos=i;
            }
            if(k<min){
                min=k;
                min_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+i));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+i));
        }
        printf("\n");
        return 0;
    }
        

    このコードは、桁が小さい場合には正しい答えが出る(はず)。しかし、一部を除くテストケースではWAになっている。なにが問題だったかというと、制約の、sの長さは1000以下というのを、26進法で4桁以下(つまり、最大でzzzz。即ち10進法の26^4-1=456975)と読み誤ってしまったことだ。しかし、サンプル(3つのうち1つがWAになっている。)を見直してみると、4桁を超えるものもある。実際には、26進法で1000桁ということだったのだ。そう読むのが問題文の素直な読み方なのだが、1000桁の数字など出るはずがないという思い込みが、上のような読み方をさせてしまったらしい。log10(26^1000)=1000*log10(26)≓1000*1.414973≓1414.9だから、これは1415桁になる。unsigned long long でも18桁までしか扱えないから、C言語の整数型で扱える範囲を遥かに超えた数になってしまう。ちなみに、RのコンソールやGoogle電卓で26^1000とか叩いてみても答えは返ってこなかった。ということは、文字列を26進法の数値とみなし10進法に直して比較するというやり方には無理があったのだ。

    すると、やはり文字列のまま比較するしかない。比較する2つの文字列の文字数は同じだから、ヌル文字がなくても、桁数分まで行ったところで比較を打ち切るようにscmp関数を書き換えれば、文字列の大小比較は可能だ。そうして、改めてCで書いたてACになったのが以下。(2024-10-23 23:35:59)

    int scmp_n(char* s, char* t, int n);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
    //    printf("s=%s\n",s);
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
        int min_pos=0, max_pos=0;
        for(i=1;i<sz;i++){
            if(scmp_n(sd+i,sd+min_pos,sz)>0){
                min_pos=i;
            }
            if(scmp_n(sd+i,sd+max_pos,sz)<0){
                max_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+i));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+i));
            }
        printf("\n");
        return 0;
    }
    int scmp_n(char* s, char* t, int n){
        int i=0;
        while(i<n){
            if (i==n-1 && *(s+i)==*(t+i)) return 0;
            else if (*(t+i)!=*(s+i)) return (*(t+i)-*(s+i));
            else i++;
        }
    }
        

    文字列についての条件判定

    ABC224-A

    問題概要

    末尾が er または ist であるような文字列 S が与えられます。S の末尾が er である場合は er を、 ist である場合は ist を出力してください。

    解答

    s=gets.chomp.split("").to_a
    sz=s.size
    #a=gets.chomp.split(" ").map(&:to_i)
    ans="er"
    if s[sz-1]=="t" then
        ans="ist"
    end
    puts ans
            

    上がコンテスト中に提出したコード。(2021-10-23 21:02:29)

    Cで書いたのが以下。(2024-10-21 22:53:48)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 22
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        if(*(s+sz-1)==114 && *(s+sz-2)==101){
            printf("%c%c\n",101,114);
        }else if (*(s+sz-1)==116 && *(s+sz-2)==115 && *(s+sz-3)==105){
            printf("%c%c%c\n",105,115,116);
        }
        return 0;
    }
        

    ABC230-B

    問題概要

    与えられた文字列は、oxx を 10^5 個結合した文字列の部分文字列か。

    解答

    n=gets.chomp.to_i
    if n>=42 then
        n+=1
    end
    printf("AGC%03d\n",n)
    

    上がコンテスト中に提出したコード。(2021-12-03 21:14:30)

    Cで書いたのが以下。(2024-11-12 00:11:16)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 12
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int sz=0;
        while(*(s+sz)==111 || *(s+sz)==120)sz++;
        char* t=(char*)malloc(sizeof(char)*12);
        if (t==NULL){
            printf("memory allocation to t failed.\n");
            exit(1);
        }
        char ss[]={111,120,120};
        for(int i=0;i<4;i++){
            for(int j=0;j<3;j++){
                *(t+i*3+j)=*(ss+j);
            }
        }
        for(int i=0;i<3;i++){
            int flag=1;
            for(int j=0;j<sz;j++){
                if(*(t+i+j)!=*(s+j)){
                    flag=0;
                    break;
                }
            }
            if(flag==1){
                printf("Yes\n");
                exit(0);
            }
        }
        printf("No\n");
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    Rubyで書いたときには馬鹿正直にoxxを10^5個繋げた文字列を作って、sがその部分文字列かどうかを調べたのだが、sは10文字以下という制約があるのだから、oxxを4個繋げた、全部で12文字の文字列を作り、その1~(sの文字数)文字目、2~(sの文字数+1)文字目、3~(sの文字数+2)文字目のいずれかと一致するかどうかを調べれば、sが最大の10文字の場合でも在り得る全パターンを調べることが出する。Cで書いたときにはそのようにして書いた。

    なお、最初は char* ss={111,120,120}; と書いたのだが、コンパイルエラーになった。そういえば、cではただポインタ宣言をするだけではメモリは確保されないからこうは書けないのだった。char ss[]={111,120,120}; と書き直して無事動いた。


    カエサル暗号

    ABC232-B

    問題概要

    文字列sをカエサル暗号により暗号化する(文字列sの全て文字を、英アルファベット26文字の環状配列でk後ろにある文字に置き換える)ことで、文字列tに一致させることができるか。

    解答

    a=gets.chomp.bytes.to_a
    b=gets.chomp.bytes.to_a
    c=[]
    a.size.times do |i|
        c[i]=a[i]-b[i]
        if c[i]<0 then
            c[i]+=26
        end
    end
    t=c[0]
    flag=true
    a.size.times do |i|
        if c[i]!=t then
            flag=false
        end
    end
    if flag==true then
        puts "Yes"
    else
        puts "No"
    end    

    上がコンテスト中に提出したコード。(2021-12-19 21:37:49)

    Cで書いたのが以下。(2024-11-20 22:36:27)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1e5+2
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* t=(char*)malloc(sizeof(char)*N1);
        if (t==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts2=fgets(t,N1,stdin);
        if(ts2==NULL){
            printf("fgets(t) failed\n");
            exit(1);
        }
        int sz=0;
        while(*(s+sz)>=97 && *(s+sz)<=122)sz++;
        int k=(*s-*t+26)%26;
        for(int i=1;i<sz;i++){
            if((*(s+i)-*(t+i)+26)%26!=k){
                printf("No");
                exit(0);
            }
        }
        printf("Yes");
        return 0;
    }
        

    カエサル暗号で一致するということは、基準となる文字からのずれが一致するということである。そう考えて今回Cでのコードを書いたが、その後でRubyで書いたときのコードを見てみると、多少の違いはあるものの同じ考え方で解いていた。多分、こういう場合にまず思いつくのは、片方の文字列の各文字を1つずつ後ろにずらした文字列を生成しつつ、それが他方の文字列と一致するかどうかを最大で26通り試すというものではないかと思うのだが、三年前の自分もそう単純ではなかったようだ。


    文字列の反転

    ABC233-B

    問題概要

    与えられた文字列のl文字目からr文字目までを反転した文字列を出力せよ。

    解答

    l,r=gets.split.map(&:to_i)
    s=gets.chomp.chars
    n=s.size
    s1=s.slice(0,l-1)
    s2=s.slice(l-1,r-l+1)
    s3=s.slice(r,n-r+1)
    s2.reverse!
    s1.concat(s2)
    s1.concat(s3)
    
    puts s1.join("") 
       

    上がコンテスト中に提出したコード。(2021-12-25 21:39:42)

    Cで書いたのが以下。(2024-11-25 22:29:42)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 15
    #define N2 1e5+2
    int readint(char *s);
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int l=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int r=readint(s1+i);
        char* s=(char*)malloc(sizeof(char)*N2);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N2,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        l--;r--;
        while(l<r){
            char temp=*(s+l);
            *(s+l)=*(s+r);
            *(s+r)=temp;
            l++;r--;
        }
        printf("%s\n",s);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    文字の入れ替え

    ABC236-A

    問題概要

    与えられた文字列のa文字目とb文字目を入れ替えたものを出力せよ。

    解答

    s=gets.chomp
    a,b=gets.chomp.split(" ").map(&:to_i)
    t=s[a-1]
    s[a-1]=s[b-1]
    s[b-1]=t
    puts s
        

    この回はコンテスト不参加。開催が日曜日だったからこの日にコンテストがあることを認識していなかったのかな。それで、今回、上のRubyのコード(2024-11-27 22:50:17)と下のCのコード(2024-11-29 22:29:44)を両方書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 12
    #define N2 6
    int readint(char *s);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int a=readint(s2);
        a--;
        int i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int b=readint(s2+i);
        b--;
        char t=*(s+a);
            *(s+a)=*(s+b);
        *(s+b)=t;
        printf("%s\n",s);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    文字のソート

    ABC242-B

    問題概要

    与えられた文字列を、文字を辞書順に並び変えて出力せよ。

    解答

    s=gets.chomp.split("")
    puts s.sort.join("")
        

    上がまずコンテスト中に提出したコード。(2022-03-05 21:15:57)

    Cで書いたのが以下。(2023-01-24 17:04:39)

    #include<stdio.h>
    #include<stdlib.h>
    #define N (2e+5)+1 
    void print_ivector(int* a,int n);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N);
        fgets(s,N,stdin);
        int n=0;
        for(int i=0;;i++){
            if((int)*(s+i)<97 || (int)*(s+i)>122)break;
            else n++; 
        }
        int a[26];
        for(int i=0;i<26;i++)*(a+i)=0;
        for(int i=0;i<n;i++){
    //        printf("%c %d\n",*(s+i),(int)*(s+i));
            *(a+((int)*(s+i)-97))+=1;
    //        a[(int)*(s+i)-97]++;
        }
        /*
        print_ivector(a,26);
        */
        for(int i=0;i<26;i++){
            for(int j=0;j<*(a+i);j++)printf("%c",i+97);
        }
        printf("\n");
        return 0;
    }
    void print_ivector(int* a,int n){
        for(int i=0;i<n-1;i++){
            printf("%d ",*(a+i));
        }
        printf("%d\n",*(a+n-1));
        /*
        for(int i=0;i<n;i++){
            printf("%d\n",*(a+i));
        }
        */
    }
        

    rubyのsortメソッドを使えば文字列でもソートできるので、すぐに終わる。一方、言語が提供するソートメソッドを使わずにやったのが上のCで書いたコード。各文字をアスキーコードに置き換えてソートするわけだが、数字の範囲がアスキーコードが97を引くと0から25に限られるので、バケツソートでソートした。


    文字列の最後の文字

    ABC244-A

    問題概要

    長さnの文字列sの最後の文字を出力せよ。

    解答

    n=gets.to_i
    s=gets.chomp
    puts s[n-1]
        

    このときは本番不参加。Rubyで書いたのが上のコード。(2023-01-14 12:49:39 )

    Cで書いたのが以下。(2024-03-08 22:49:19)

    #include<stdio.h>
    #include<stdlib.h>
    #define N 6
    #define N2 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N);
        if(s==NULL){
            printf("memory allocation to s failed\n");
            exit(1);
        }
        char* ts1=fgets(s,N,stdin);
        if(ts1==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        free(s);
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to s2 failed\n");
            exit(1);
        }
        char* ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        printf("%c\n",*(s2+n-1));
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC248-A

    問題概要

    与えられた文字列には、0~9の数字のうち1つだけが現れず、他の9つは1度ずつ出現する。現れない数字を出力せよ。

    解答

    s=gets.chomp.split("").to_a.map(&:to_i)
    a=(0..9).to_a
    b=a-s
    puts b[0]
        

    上がコンテスト中に提出したコード。(2022-04-16 21:02:40)

    Cで書いたのが以下。(2025-02-12 22:11:21)

    #define N1 11
    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if(s1==NULL){
            printf("memory allocation to s1 failed\n");
            exit(1);
        }
        fgets(s1,N1,stdin);
        for(char i=48;i<=57;i++){
            int flag=0;
            for(int j=0;j<9;j++){
                if(*(s1+j)==i){
                    flag=1;
                    break;
                }
            }
            if(flag==0){
                printf("%c\n",i);
                exit(0);
            }
        }
        return 0;
    }
        

    Rubyで書いたときは、出てくる数字の配列を作り、それを 0..9 を要素とする配列から引いた作って出来た数列の唯一の要素を答えとしている。一方、Cで書いたときは、 0..9 のそれぞれについて与えられた文字列に出てくるかどうかを調べ、出てこないものが見つかったらそれを答えとしている。

    2025年2月17日月曜日

    その他のA問題(ABC236-250)

    ABC238-A

    問題概要

    2^n > n^2 か。

    解答

    n=gets.to_i
    if n==1 || n>4 then
        puts "Yes"
    else
        puts "No"
    end
        

    上がコンテスト中に提出したコード。(2022-02-05 21:11:21)

    Cで書いたのが以下。(2024-12-10 11:39:44)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 12
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        free(s);
        if(n==1 || n>4){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    グラフを書いてみると明らかなように、nが少し大きくなると指数関数の方が平方よりもずっと大きくなる。(高2の頃にやったことだ。実数 x について極限を取ると、a \gt 1 のとき、 \lim_{x \to ∞}\frac{a^x}{x^a} = ∞ である。)

    n2^nn^2
    121
    244
    389
    41616
    53225
    66436
    712849
    825664
    951281
    101024100

    n=10までを表にすると上のようになる。常に 2^n の方が大きくなるのは n=5 からだが、その前に n=1 の時にも 2^n の方が大きくなっていることに注意が必要。Rubyで書いたコードもCで書いたコードも上の表を念頭に場合分けして書いている。制約上nは最大で10^9なので、実際に2^nやn^2を計算するのはRubyでならともかくCでは無理。n=32で既に2^nはint型で扱える範囲を超えてしまうのだ。


    確率

    ABC242-A

    問題概要

    1からnまでの数がある。この数から、小さい順にa個選び、続いて、a+1からbまでの数のうちc個をランダムに選ぶ。ある数xが選ばれる確率を求めよ。

    解答

    a,b,c,x=gets.chomp.split(" ").map(&:to_i)
    p=0.0
    if x<=a
        p=1.0
    elsif
        x>b
        p=0.0
    else
        p=c/(b-a).to_f
    end
    puts p
        

    上がまずコンテスト中に提出したコード。(2022-03-05 21:08:04)

    ところで、この日はなぜかA問題でコンテスト中に同じRubyのコードを2回回提出している。その二番目が以下。(2022-03-05 21:09:46)

    a,b,c,x=gets.chomp.split(" ").map(&:to_i)
    p=0.0
    if x<=a
        p=1.0
    elsif
        x>b
        p=0.0
    else
        p=c/(b-a).to_f
    end
    puts p
        

    先のコードと比べると、xがa以下かbより大のときに代入する1と0が整数か小数かの違いだけなのだが、それだけで実行速度が61ms から 137ms へと大幅に遅くなっている。

    Cで書いたのが以下。(2025-01-03 19:35:10)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 21
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int a=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        i++;
        while(*(s+i)!=32)i++;
        i++;
        int c=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int x=readint(s+i);
        free(s);
    //    printf("a=%d b=%d c=%d x=%d\n",a,b,c,x);
        double p=0;
        if (x<=a)p=1.0;
        else if (x<=b)p=(double)c/(b-a);
        printf("%lf\n",p);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    周期算

    ABC243-A

    問題概要

    初期値がVの数値を、3個単位周期の数列a,b,c,a,b,c,...に従い、k回目にはその数列の第k項の数を減算していく。数値が0未満になるのはいつか。(それぞれ数aを減算した時に0未満になるならばF,bを減算した時ならばM,cを減算した時ならばTを出力。)

    解答

    v,a,b,c=gets.chomp.split(" ").map(&:to_i)
    t=a+b+c
    v=v%t
    if v<a
        puts "F"
    elsif v<a+b
        puts "M"
    else
        puts "T"
    end
        

    上が、コンテスト中に提出したコード。(2022-03-12 21:06:08)

    Cで書いたのが以下。(2025-01-03 21:15:30 )

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 29
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int v=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        i++;
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int c=readint(s+i);
        free(s);
    //    printf("a=%d b=%d c=%d v=%d\n",a,b,c,v);
        int t=a+b+c;
        int r=v%t;
        if (r-a<0)printf("F");
        else if (r-a-b<0)printf("M");
        else printf("T");
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        


    ABC245-A

    問題概要

    a時b分とc時d分1秒とでどちらが早い時刻か判定せよ。

    解答

    a=gets.chomp.split(" ").map(&:to_i)
    ta=a[0]*3600+a[1]*60
    ao=a[2]*3600+a[3]*60+1
    if ta<ao 
        puts "Takahashi"
    else
        puts "Aoki"
            end
        

    これが、コンテスト中に提出したコード。(2022-03-26 21:06:19)

    Cで書いたのが以下。(2025-01-04 23:42:51)


    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 21
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int a=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        i++;
        while(*(s+i)!=32)i++;
        i++;
        int c=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int d=readint(s+i);
        free(s);
    //    printf("a=%d b=%d c=%d d=%d\n",a,b,c,d);
        int taka=3600*a+60*b;
        int ao=3600*c+60*d+1;
        if(taka<ao)printf("Takahashi\n");
        else printf("Aoki\n");
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        


    場合分け

    ABC249-A

    問題概要

    1. 甲は「A 秒間 B (m/s)で進み、C 秒間停止する」ことを繰り返す。
    2. 乙は「D 秒間 E (m/s)で進み、F 秒間停止する」ことを繰り返す。
    甲、乙が同時に出発してからX秒後、どちらがより大きな距離を進んでいるか。

    解答

    a,b,c,d,e,f,x=gets.chomp.split(" ").to_a.map(&:to_i)
    taka=0
    aoki=0
    n1,r1=x.divmod(a+c)
    if r1>=a
        taka=(n1+1)*a*b
    else
        taka=(n1*a+r1)*b
    end
    t2=0
    n2,r2=x.divmod(d+f)
    if r2>=d
        aoki=(n2+1)*d*e
    else
        aoki=(n2*d+r2)*e
    end
    #p [taka,aoki]
    if taka==aoki
        puts "Draw"
    elsif taka>aoki
        puts "Takahashi"
    else
        puts "Aoki"
    end    
        

    上がコンテスト中に提出したコード。(2022-04-23 21:25:57)

    Cで書いたコードは以下。(2025-02-12 22:50:12)

    #define N1 29
    int readint(char *s);
    int min(int a,int b);
    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if(s1==NULL){
            printf("memory allocation to s1 failed\n");
            exit(1);
        }
        fgets(s1,N1,stdin);
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int c=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int d=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int e=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int f=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int x=readint(s1+i);
    //    printf("a=%d b=%d c=%d d=%d e=%d f=%d x=%d\n",a,b,c,d,e,f,x);
        int q1=x/(a+c); int r1=x%(a+c);
        int q2=x/(d+f); int r2=x%(d+f);
        int takahashi=q1*a*b+min(r1,a)*b;
        int aoki=q2*d*e+min(r2,d)*e;
    //    printf("Takahashi=%d\n",takahashi);
    //    printf("Aoki=%d\n",aoki);
        if(takahashi>aoki){
            printf("Takahashi\n");
        }else if(takahashi==aoki){
            printf("Draw\n");
        }else{
            printf("Aoki\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    int min(int a,int b){
        if(a<b)return a;
        else return b;
    }
        

    中学受験の周期算でも割とあるパターンの問題なのだが、こういう、「進む」「休む」を繰り返す場合には、進んだ時間と休んだ時間をまとめて一周期として、これが何周期あるかを考える。まず甲について、進んだ距離を計算する。全体の時間xを一周期の時間(a+c)で割り、商がnになったとして、余りの時間が一周期に進む時間a以上ならば、全体としてn+1周期分進んでいることになる。余りがaよりも小さければ、それは端数の時間として秒単位で加算して、全体でn周期分+(x%(a+c))秒間進んでいることになる。最後はこの秒数に秒速をかければ進んだ距離が求められる。乙についても同様に進んだ距離を求め、両者を比較すればよい。

    2025年1月13日月曜日

    その他のA問題(ABC221-235)


    四捨五入

    ABC226-A

    問題概要

    与えられた実数(小数第三位まで表示)を小数第一位で四捨五入し、整数として出力せよ。

    解答

    f=gets.chomp.to_f
    puts f.round
        

    上がコンテスト中に提出したコード。(2021-11-07 21:16:08)

    Cで書いたのが以下。(2024-10-25 22:35:03)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 9
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int ans=0,i=0;
        while(*(s+i)>=48 && *(s+1)<=57){
            ans*=10;
            ans+=*(s+i)-48;
            i++;
        }
        if(*(s+i)!=46){
            printf("%d\n",ans);
            exit(0);
        }
        i++;
        if(*(s+i)>52)ans+=1;
        printf("%d\n",ans);
        return 0;
    }
        

    単純計算

    ABC231-A

    問題概要

    与えられた整数を1/100倍して出力せよ。

    解答

    d=gets.chomp.to_f
    puts d/100    
        

    上がコンテスト中に提出したコード。(2021-12-11 21:03:03)

    Cで書いたのが以下。(2024-11-13 23:03:33)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int readint(char *s);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int d=readint(s);
        printf("%f\n",(double)d/100);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    単純計算

    ABC232-A

    問題概要

    a,bを1以上9以下の整数とし、axbという文字列が与えられる。a*bを計算して出力せよ。

    解答

    a=gets.chars
    puts a[0].to_i * a[2].to_i    
        

    上がコンテスト中に提出したコード。(2021-12-19 21:16:37)

    Cで書いたのが以下。(2024-11-20 22:24:53)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int ans=(*s-48)*(*(s+2)-48);
        printf("%d\n",ans);
        return 0;
    }
        

    ABC233-A

    問題概要

    aをb以上にするためには、aに10を最小で何回加算する必要があるか。

    解答

    a,b=gets.split.map(&:to_i)
    i=0
    while a+10*i < b do
        i+=1
    end
    puts i    

    上がコンテスト中に提出したコード。(2021-12-25 21:47:22)

    Cで書いたのが以下。(2024-11-20 22:50:44)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 11
    int readint(char *s);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int x=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int y=readint(s+i);
        int ans=0;
        if(x<y){
            int d=y-x;
            if(d%10==0)ans=d/10;
            else ans=(d/10)+1;
        }
        printf("%d\n",ans);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    今回cで書いたコードは、まずbとaの差を求め、それを10で割った商を答えとするというのを基本とするが、その際、差を10で割ったときに余りが出るかどうかで場合分けが必要なのが要注意である。一方、以前にRubyで書いたときのやり方は、aが10未満である限りaに10を足すことを繰り返してその回数を数えるというもので、bとaの差が大きいときには計算回数が多くなってしまうという難点はあるものの、場合分けは必要ない。


    ABC234-A

    問題概要

    関数 f を f(x)=x^2+2x+3 と定義します。整数 t が入力されるので、 f(f(f(t)+t)+f(f(t))) を求めてください。

    解答

    def f(x)
        x*x+2*x+3
    end
    t=gets.to_i
    
    puts f(f(f(t)+t)+f(f(t)))
        

    上がコンテスト中に提出したコード。(2022-01-08 21:07:34)

    Cで書いたのが以下。(2024-11-25 22:39:10)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 4
    int readint(char *s);
    int f(int x);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int t=readint(s);
        printf("%d\n",f(f(f(t)+t)+f(f(t))));
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    int f(int x){
        return x*x+2*x+3;
    }
        

    計算の工夫

    ABC235-A

    問題概要

    3 つの数字 x,y,z をこの順に並べてできる 3 桁の整数を xyz と表すことにします。どの桁も 0 でない 3 桁の整数 abc が与えられるので、abc+bca+cab を求めてください。

    解答

    s=gets.chomp.split("")
    a,b,c=s[0],s[1],s[2]
    puts (a+b+c).to_i+(b+c+a).to_i+(c+a+b).to_i    

    上がコンテスト中に提出したコード。(2022-01-15 21:06:13)

    Cで書いたのが以下。(2024-11-27 22:19:15)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int a=*s-48;
        int b=*(s+1)-48;
        int c=*(s+2)-48;
        printf("%d\n",(a+b+c)*111);
        return 0;
    }
        

    中学受験算数でもよく見かける気のするタイプの問題である。勿論、馬鹿正直に3つの数を足してもよいのだが、(100a+10b+c)+(100b+10c+a)+(100c+10a+b)=100(a+b+c)+10(a+b+c)+(a+b+c)=111(a+b+c)であることに気づけば、最後の掛け算の筆算はabcを一つずつずらして書いて足せばいいだけなので楽にできる。コンピュータを使う場合にはその方法を使う意味はそれほどないが、やはり何の工夫もないコードを書くよりはいいだろうと思って書いたのが上のコード。

    一方、敢えて問題文の通りに文字を入れ替えたものを整数として読み込んで足すという方法もある。コードとしてはこちらの方が面白いかもしれない。それで書いたのが以下。(2024-11-27 22:26:06)前にRubyで書いたときも同じようなやり方をしていた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int readint(char *s);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int x1=readint(s);
        char t=*s;
        *s=*(s+1);
        *(s+1)=*(s+2);
        *(s+2)=t;
        int x2=readint(s);
        t=*s;
        *s=*(s+1);
        *(s+1)=*(s+2);
        *(s+2)=t;
        int x3=readint(s);
        printf("%d\n",x1+x2+x3);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    2024年12月30日月曜日

    その他のA問題(ABC211-220)

    単純計算

    ABC211-A

    問題概要

    与えられた整数a,bに対し、実数 (A−B)/3+B を計算せよ。

    解答

    #n=gets.chomp.to_i
    a,b=gets.chomp.split(" ").map(&:to_i)
    ans=(a+2*b)/3.0
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-24 21:05:33)

    Cで書いたコードが以下。(2024-05-22 22:46:09)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 8
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int i=0;
        int a=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
    //    printf("a=%d b=%d\n",a,b);
        double c=(a-b)/(double)3+b;
        printf("%lf\n",c);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC212-A

    問題概要

    与えられた2数のうち一方は0か。

    解答

    #n=gets.chomp.to_i
    ans=""
    a,b=gets.chomp.split(" ").map(&:to_i)
    if 0<a and b==0 then
        ans="Gold"
    elsif  a==0 and 0<b then
        ans="Silver"
    else
        ans="Alloy"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-31 21:04:02)

    Cで書いたコードが以下。(2024-05-24 22:40:00)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 9
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int i=0;
        int a=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
    //    printf("a=%d b=%d\n",a,b);
        if (a>0 && b==0){
            printf("Gold\n");
        }else if (a==0 && b>0){
            printf("Silver\n");
        }else if(a>0 && b>0){
            printf("Alloy\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    数値の範囲の条件判定

    ABC214-A

    問題概要

    与えられた整数が125以下ならば4、126以上211以下ならば6、212以上ならば8を出力。

    解答

    n=gets.chomp.to_i
    ans=4
    if n>=126 and n<=211 then
        ans=6
    elsif n>=212 then
        ans=8
    end
    puts ans
        

    上がコンテスト中に提出したコード(2021-08-14 21:04:53)

    Cで書いたコードが以下。(2024-06-10 22:35:18)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int n=readint(s1);
        int ans=0;
        if(n<126)ans=4;
        else if(n<212)ans=6;
        else ans=8;
        printf("%d\n",ans);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        


    条件判定、出力形式

    ABC216-A

    問題概要

    小数部分が1桁の実数が与えられる。整数部分の後ろに、その小数第1位の数が2以下ならば-、7以上ならば+をつけて出力せよ。

    解答

    x,y=gets.chomp.split(".")
    y=y.to_i
    ans=x
    if y<3
        ans+="-"
    elsif y>=7
        ans+="+"
    end
    puts ans
        

    この回は本番不参加。コンテストが日曜開催だったため、この日にコンテストがあることを把握できていなかった。

    Rubyで書いたのに続けてCで書いたが、その間AtCoderの過去問を解くのをお休みしていたため、両コードの間で3カ月余り時間が空いている。

    上がrubyで書いたコード。(2024-06-26 22:40:43)

    Cで書いたのが以下。(2024-10-02 22:33:01)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 6
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int x,y;
        int i=0;
        int s=readint(s1+i);
        while(*(s1+i)!=46)i++;
        if(i==1){
            x=*s1-48;
        }else if(i==2){
            x=(*s1-48)*10+(*(s1+1)-48);
        }else{
            printf("error\n");
        }
        i++;
        y=readint(s1+i);
    //    printf("x=%d y=%d\n",x,y);
        printf("%d",x);
        if(y<3){
            printf("%c\n",45);
        }else if(y<7){
            printf("\n");
        }else{
            printf("%c\n",43);
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    条件分岐

    ABC219-A

    問題概要

    ①0 点以上 40 点未満、②40 点以上 70 点未満、③70 点以上 90 点未満、④90 点以上、とランクが分かれている。ある点数が与えられるが、上位のランクになるためには最低あと何点必要か。ただし、既に90 点以上の場合はexpertと出力。

    解答

    x=gets.chomp.to_i
    #s=gets.chomp.split("").to_a
    ans=0
    if x>=90 then
        puts "expert"
    elsif x>=70 then
        puts 90-x
    elsif x>=40 then
        puts 70-x
    else
        puts 40-x
    end
        

    上がコンテスト中に提出したコード。(2021-09-18 21:05:36)

    Cで書いたのが以下。(2024-10-09 22:22:40)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int x=readint(s1);
        if(x>=90){
            printf("expert\n");
        }else if (x>=70){
            printf("%d\n",90-x);
        }else if (x>=40){
            printf("%d\n",70-x);
        }else {
            printf("%d\n",40-x);
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    2024年12月27日金曜日

    その他のA問題(ABC201-210)


    ABC202-A

    問題概要

    3つの整数(1~6)が与えられる。7から各整数を引いた数の和を出力せよ。

    解答

    a,b,c=gets.chomp.split(" ").map(&:to_i)
    puts (7-a)+(7-b)+(7-c)
        

    この時はコンテスト不参加。前の週に起こった事件のため、この日はコンテストに参加するのが怖かったのだ。上のrubyのコード(2024-04-12 22:43:48)と下のCのコード(2024-04-12 22:50:34)は同時に書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int c=readint(s1+i);
        free(s1);
        printf("%d\n",21-a-b-c);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    プロトタイプ宣言を書き忘れていることに後になって気付いたが、なぜかそれでもちゃんと動いた。(202-A, 203-Aも同様。203-Bに至って初めてエラーになった。


    ABC203-A

    問題概要

    a,b,c の3つの整数が与えられる。そのうち2つの数が同じであれば残り1つの数を、同じものがなければ0を出力せよ。

    解答

    #n=gets.chomp.to_i
    a,b,c=gets.chomp.split(" ").map(&:to_i)
    ans=0
    if a==b then
        ans=c
    elsif b==c then
        ans=a
    elsif c==a then
        ans=b
    end
    
    puts ans
        

    上がコンテスト中に提出したコード。(2021-05-30 21:06:27)(冒頭に整数一つ読み込み用のコードをコメントアウトしながら残しているあたり、前はこんなことしてたなと思う。)

    Cで書いたのが以下。(2024-04-15 22:49:15)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int c=readint(s1+i);
        free(s1);
        int ans=0;
        if (a==b)ans=c;
        else if (b==c)ans=a;
        else if (c==a)ans=b;
        printf("%d\n",ans);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC204-A

    問題概要

    3人がじゃんけんをしてあいこになった。二人の手が与えられるので、残り一人の手を出力せよ。

    解答

    #n=gets.chomp.to_i
    a,b=gets.chomp.split(" ").map(&:to_i)
    ans=0
    if a==b then
        ans=a
    else
        if a==1 && b==2 || b==2 && a==1 then
        ans=0
        elsif a==0 && b==1 || a==1 && b==0 then
        ans=2
        elsif a==0 && b==2 || a==2 && b==0 then
        ans=1
        end
    end
    puts ans
        

    与えられた二つの手が同じであれば、残り一つの手もそれと同じ。異なれば、残り一つの手はその二つとは異なるものである。高校数学の場合の数・確率でじゃんけんをして相子になる確率とか求め慣れているのですぐに分かる。

    上がコンテスト中に提出したコード。(2021-06-06 21:07:04)

    Cで書いたのが以下。(2024-04-17 22:48:11)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
        if(a==b){
            printf("%d\n",a);
        }else{
            if(a==0 && b==1 || a==1 && b==0)printf("2\n");
            else if(a==0 && b==2 || a==2 && b==0)printf("1\n");
            else if(a==1 && b==2 || a==2 && b==1)printf("0\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC205-A

    問題概要

    100単位当たりaの値を持つものがある。これのb単位の値を求めよ。

    解答

    本格的にコードを書き始めてから比較的日の浅い時期のものであるため、この時のコンテスト中にの行動は今にしてみるとかなり変則的である。まず、A問題にRubyで解答してWAになり、続いてB問題にRubyで解答してTLEになり、続いてA問題にC++で解答してWAになり、次にB問題にRubyで解答してやっとACになっている。その後、A問題にC++で解答してACになっている。

    まず、コンテスト中に提出したC++のコードを掲げる。(2021-06-13 21:27:16)

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int a,b;
        double c=0;
        cin>>a>>b;
        c=(double)a*b/100;
        cout<<c<<endl;
        return 0;
    }
        

    次に示すのは、コンテスト中に最初に提出してWAになったrubyのコード。(2021-06-13 21:02:47)

    a,b=gets.chomp.split(" ").map(&:to_i)
    
    ans=a*b/100
    puts ans
        

    計算の際に整数のまま計算しているため、小数点以下を切り捨てた演算が行われている。これを解決するためには、a,b,100のいずれかに.to_fをつければよい。(整数のまま計算できるところは整数のままにしておいた方が効率がいいから、.to_fは最後に除算を行う100につけるのが一番いいだろう。)C++のコードでは浮動小数点数への変換をちゃんと行っているから、その必要性に気づかなかったわけではないのだろうが、なぜrubyでの解答を放棄したのか、今となっては謎。

    Cで書いたのが以下。(2024-04-19 23:32:54)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 11
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
    //    int c=a*b;
    //    printf("%d %d %d\n",a,b,c);
        printf("%lf\n",a*b/(double)100);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    初めに書いたコードでは、いくつかのテストケースでWAになった。最初は浮動小数点数の誤差が原因かと思ったが、WAになったテストケースの一つ、max_00を見ると、入力は 1000 1000 で、これに対する答えは10000でなければならないはずだが、なぜか10.000000 が出力されてしまう。また、テストケースtext_00は入力が 844 535 で、これに対する答えは4515.4 でなければならないが、447.320000が出力されてしまう。double型の演算になにか自分が知らない陥し穴があるのかと悩み、いろいろ調べてみたが、そんなものはなさそう。(普段整数型しか使わないから、浮動小数点数を使って変なことが起こると自分の知らない何かがあるのかと無用に悩んでしまう。)結局、原因は、最初に書いたコードでは文字列の読み込みの際に11バイト必要なところ7バイトしか読み込んでいなかったのが原因だと判明。前に書いたコードをそのままコピーしてペーストしていて、この問題に合わせて読むこむバイト数を変えることを忘れていたのだ。また、入力が正しく読み込めているかの確認を怠っていたことも、原因の究明が遅れた理由の一つだった。


    ABC206-A

    問題概要

    与えられた数字に1.08を乗じた数を超えない最大の整数が206と比べて小さければYay!、等しければso-so、大きければ:(と出力せよ。

    解答

    ans=""
    n=gets.chomp.to_i
    n=(n*1.08).floor
    if n<206 then ans="Yay!"
    elsif n==206 then ans="so-so"
    else ans=":("
    end
    #a=gets.chomp.split(" ").map(&:to_i)
    puts ans
        

    上がコンテスト中に提出したコード。(2021-06-19 21:05:59)

    Cで書いたコードが以下。(2024-04-26 23:46:33)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 5
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int n=readint(s1);
        n=(int)(n*1.08);
        if(n<206)printf("Yay!\n");
        else if (n==206)printf("so-so\n");
        else printf(":(\n");
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    ABC207-A

    問題概要

    与えられた3つの数のうち2つの和として考えられる最大値を求めよ。

    解答

    ans=0
    #n=gets.chomp.to_i
    a=gets.chomp.split(" ").map(&:to_i)
    a.sort!
    ans=a[2]+a[1]
    puts ans
        

    上がコンテスト中に提出したコード。(2021-06-26 21:03:04)

    Cで書いたコードが以下。(2024-05-03 23:38:58)

    int tri_min(int a,int b,int c);
    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 13
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int c=readint(s1+i);
        printf("%d\n",a+b+c-tri_min(a,b,c));
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    int tri_min(int a,int b,int c){
        int min;
        if(a<b){
            if(c<a){
                min=c;
            }else{
                min=a;
            }
        }else{
            if(c<b){
                min=c;
            }else{
                min=b;
            }
        }
        return min;
    }
        

    かなり簡単な問題なので、rubyで書いたコードはコードを書き始めて日の浅いこの時期でも3分で書き上げている。一方、Cでのコードはエラー無く動くコードの完成まで30分以上かかった。もっとも、今回の場合、実行エラーの原因はコードそのものではなく、入力データが前回のもののままだったことが原因だったのだが、それも含めて、Cではrubyではほとんど問題にならないような標準入力からのデータの受取時点で躓くことが多いことを改めて実感する。最近はCで書いたコードの蓄積も増えてきたので、入力データの受取部分もほとんどコピー&ペーストで済むが(とはいえそれがエラーの原因になることもあるから要注意だ)、それでもCで書くと

    この部分にかなりの手間を取られるのである。逆に、そこを越えれば、Cでもrubyでもほとんど同じようなコードが書ける。また、エラーの原因の発見のためには、入力されたデータを表示するデバッグ出力コードを埋め込むことは省略してはいけないことを改めて実感した。


    ABC208-A

    問題概要

    1~6の目が出る骰子をa回振る。出目の合計がbになるとがあるか。

    解答

    ans=""
    #n=gets.chomp.to_i
    a,b=gets.chomp.split(" ").map(&:to_i)
    if b>=a and b<=a*6 then
        ans="Yes"
    else
        ans="No"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-04 21:12:05)

    Cで書いたのが以下。(2024-05-10 22:46:51)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
    //    printf("a=%d b=%d\n",a,b);
        if(b>=a && b<=6*a){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    今回Cで書いたとき、\sum_{i=1}^{a} a_i =b を満たす数列 a (1≤a_i≤6) が存在するか判定するということか。そうすると、単純に考えれば6^(a-1)回のループを回さないといけない。((a-1)回目までの目が決まれば、あとはb-\sum_{i=1}^{a-1} a_i が1以上6以下かを調べればよい。)制約によりaは最大で100だから、log10(6^99)=99*log10(6)≓77。10^77回の計算などできるわけない。すると動的計画法とかを使わないといけないのか。しかしA問題でそんなものが出るはずはない。だいたい、3年前の自分はこの問題が解けたのか。としばらく悩んだ。

    だが、やがて、解法は意外と単純なことに気づく。6までの目が出る骰子をa回振ると、出目の合計は全ての6が出た場合に最大となり、この時6*aである。ここで、1回だけ5の目が出たとすると、出目の合計はそれより1小さい数になる。同様に考えると、出目の合計は、1*a以上6*a以下の全ての自然数をとり得ることになる。3年前にrubyで書いたコードは見ずに書いたが、後で見てみると、今回と全く同じ判定方法をとっていた。


    ABC209-A

    問題概要

    2つの整数a,bが空白区切りで与えられる。a以上b以下の整数はいくつあるか。

    解答

    a,b=gets.chomp.split(" ").map(&:to_i)
    if b>=a then
    puts b-a+1
    else
        puts 0
    end
        

    上がコンテスト中に提出したコード。(2021-07-10 21:41:06)

    以下がCで書いたコード。(2024-05-17 22:28:18)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 9
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int a=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int b=readint(s1+i);
    //    printf("a=%d b=%d\n",a,b);
        printf("%d\n",max(b-a+1,0));
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    int max(int a,int b){
        if (a>b)return a;
        else return b;
    }
        

    プロトタイプ宣言を書き忘れたが動いた。

    a以上b以下の整数は b-a+1 であるというのは、小学校4年生くらいで常識にしておいてほしい。植木算と同じことと考えてもよいが、オタク浪人としては、「b以下の整数の数から、a-1 までの整数の数を引いて、b-(a-1)=b-a+1」という考え方の方が好き。整数の数でなくても、等差数列の項数ならば同様に考えることができて、例えば、50以上100以下の偶数の数ならば、(100-50)/2+1=51 である。


    ABC210-A

    問題概要

    1個当たりの価格は x である商品がある。これを a 個より多く購入すると a個については価格が x であるが、a個より多く購入した部分については1個当たりの価格が y になる。(xはyより小) n個購入するために必要な金額を求めよ。

    解答

    #n=gets.chomp.to_i
    n,a,x,y=gets.chomp.split(" ").map(&:to_i)
    ans=0
    if n<=a then
        ans=n*x
    else
        ans=a*x+(n-a)*y
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-17 21:07:59)

    Cで書いたコードが以下。(2024-05-20 22:48:21)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 23
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
        int n=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int a=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int x=readint(s1+i);
        while(*(s1+i)!=32)i++;
        i++;
        int y=readint(s1+i);
    //    printf("n=%d a=%d x=%d y=%d\n",n,a,x,y);
        if(n<=a){
            printf("%d\n",n*x);
        }else{
            printf("%d\n",a*(x-y)+n*y);
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
        

    2022年11月28日月曜日

    配列の検索、インデックスを取得など

    最大値のインデックスをすべて取得

    ABC252-B

    問題概要

    数列aの最大値が、数列bに含まれる数をインデックスとしているか。

    解答

    n,k=gets.chomp.split(" ").to_a.map(&:to_i)
    a=gets.chomp.split(" ").to_a.map(&:to_i)
    b=gets.chomp.split(" ").to_a.map(&:to_i)
    h=Hash.new
    a.each do |item|
        if h.has_key?(item)
            h[item]+=1
        else
            h[item]=1
        end
    end
    amax=h.keys.max
    amax_idx=[]
    a.each_with_index do |item,i|
        amax_idx.push(i) if item==amax
    end
    b.each do |item|
        amax_idx.each do |idx|
            if item==idx+1
                puts "Yes"
                exit
            end
        end
    end
    puts "No"
        

    「正の字カウント」で、aの各値の出現回数を求める。(今回は出現回数自体は必要ないのだが、既存のコードの流用のため。)そして、もう一度ループを回して、aの最大値のインデックスを配列amax_idxを作る。あとは、amax_idxとbに共通する要素があるか見ていけばよい。

    Cで書いたのが以下。

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 2e9+2
    void print_ivector(int* a,int n);
    int main(void){
        int n,m;
        int min_idx,max_idx,min=MAX,max=0;
        int max_count=0,min_count=0;
        scanf("%d %d\n",&n,&m);
    //    printf("n=%d, m=%d\n",n,m);
        int* a=(int*)malloc(sizeof(int)*n);
        int* b=(int*)malloc(sizeof(int)*m);
        int* c=(int*)malloc(sizeof(int)*n);
        int* d=(int*)malloc(sizeof(int)*n);
        for(int i=0;i<n;i++)scanf("%d",a+i);getchar();
        for(int i=0;i<m;i++)scanf("%d",b+i);getchar();
    /*
        printf("a={");
        print_ivector(a,n);
        printf("}\n");
        printf("b={");
        print_ivector(b,m);
        printf("}\n");
    */
    
        for(int i=0;i<n;i++){
            *(c+i)=-1;
            *(d+i)=-1;
        }
        for(int i=0;i<n;i++){
            if (*(a+i)>max){
                max=*(a+i);
                max_idx=i;
                for(int i=0;i<=max_count;i++){
                    *(c+i)=-1;
                }
                *c=i;
                max_count=1;
            }else if (*(a+i)==max){
                *(c+max_count)=i;
                max_count++;
            }
            if(*(a+i)<min){
                min=*(a+i);
                min_idx=i;
                for(int i=0;i<=min_count;i++){
                    *(d+i)=-1;
                }
                *d=i;
                min_count=1;
            }else if (*(a+i)==min){
                *(d+min_count)=i;
                min_count++;
            }
        }
    //    printf("max : %d, %d個\n",max,max_count+1);
    //    printf("min : %d, %d個\n",min,min_count+1);
        for(int i=0;i<m;i++){
            for(int j=0;j<max_count;j++){
                if(*(b+i)==*(c+j)+1){
                    printf("Yes\n");
                    exit(0);
                }
            }
        }
        printf("No\n");
        return 0;
    }
    void print_ivector(int* a,int n){
        /*
        for(int i=0;i<n;i++){
            printf("%d\n",*(a+i));
        }
        */
        for(int i=0;i<n-1;i++){
                printf("%d ",*(a+i));
            }
        printf("%d\n",*(a+n-1));
    }
        

    最大値と最小値のインデックスを保持する配列を作るところはRubyでやった場合と同じだが、このように最初に長さが分からない配列を作るのがCではやりにくい。最大値または最小値の個数は最大でaの要素数に等しいので、今回は、aの同じ長さの配列を作り、それに現在まで見つかったインデックスを入れていき、最大値が更新された時にはそれを初期値の-1に戻すというやりかたでやった。


    最大値・最小値のインデックス

    ABC275-A

    問題概要

    整数を要素とする配列が与えらたる。その最大値のインデックスを求めよ。

    解答

    最大値・最小値を求めるのは基本中の基本だが、その際にインデックスも更新していけばよい。

            n=gets.to_i
    a=gets.chomp.split(" ").map(&:to_i)
    idx=0
    max=0
    n.times do |i|
        if a[i]>max
            max=a[i]
            idx=i
        end
    end
    puts idx+1
        

    これは本番中の提出コード。

    Cで書いたのが以下。問題では最大値のインデックスだけ求めればよいが、今後使うことも考えて最小値のインデックスも一緒に求めるコードを書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 2e9+2
    int main(void){
        int n,min_idx,max_idx,min=MAX,max=0;
        scanf("%d\n",&n);
        int* a=(int*)malloc(sizeof(int)*n);
        for(int i=0;i<n;i++){
            scanf("%d",a+i);
            if (*(a+i)>max){
                max=*(a+i);
                max_idx=i;
            }else if(*(a+i)<min){
                min=*(a+i);
                min_idx=i;
            }
        }
        printf("%d\n",max_idx+1);
    //    printf("%d\n",min_idx+1);
        return 0;
    }
        

    より汎用性を求めるなら、インデックスと最大値・最小値を配列で返す関数を作っておいてもいい。


    最大値または最小値が複数あって、そのインデックスをすべて答える必要がある場合

    ans=[]
    n.times do |i|
        if cc[i]>max
            ans.clear
            max=cc[i]
            ans.push(i)
        elsif cc[i]==max
            ans.push(i)
        end
    end
    ans.size.times do |i|
        puts ans[i]+1
    end
        

    最大値が更新されたときに、インデックスが格納された配列をクリアしてそのインデックスを入れる、もし最大値と同じ値がでてきたら、そのインデックスを答えの配列に追加する、としていけばよい。


    後ろから数えたインデックス

    ABC276-A

    問題概要

    英小文字からなる文字列sが与えられる。sに文字aが現れるならば最後に現れるのが何文字目かを出力し、現れないならば−1を出力

    文字列を分割して配列にし、末尾から順に見ていけばいい。

    解答

    a=gets.chomp.split("").to_a
    s=a.size
    s.downto(0).each do |i|
        if a[i]=="a"
            puts i+1
            exit
        end
    end
    puts -1
        

    これが本番で提出したコード

    Cでやったのが以下。

    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
        int n,m,ans=0;  
        scanf("%d %d\n",&n,&m);
        int* a=(int*)malloc(sizeof(int)*n);
        for(int i=0;i<n;i++){
            scanf("%d",a+i);
            if(*(a+i)==m){
                ans=i+1;
                break;
            }
        }
        printf("%d\n",ans);
        return 0;
    }
        

    要素の出現位置(前から数えたインデックス)を調べる

    これは、最も基本的な問題。

    ABC277-A

    問題概要

    n個の数字からなる配列が与えられる、mが何番目に出現するか求めよ。

    n,k=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
    a=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
    idx=0
    n.times do |i|
        if a[i]==k
            idx=i
            break
        end
    end
    puts idx+1
        

    これが本番で提出したコード。

    Cでやったのが以下。

    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
        int n,m,ans=0;  
        scanf("%d %d\n",&n,&m);
        int* a=(int*)malloc(sizeof(int)*n);
        for(int i=0;i<n;i++){
            scanf("%d",a+i);
            if(*(a+i)==m){
                ans=i+1;
                break;
            }
        }
        printf("%d\n",ans);
        return 0;
    }
        

    ABC297-A

    問題概要

    配列の隣接する要素の差が初めて一定値以下になる箇所を出力せよ。

    解答

    n,d=gets.chomp.split(" ").map(&:to_i)
    t=gets.chomp.split(" ").map(&:to_i)
    (1...n).each do |i|
        if t[i]-t[i-1]<=d
            puts t[i]
            exit
        end
    end
    puts -1
        

    上が本番で提出したコード。以下はCで書いたもの。

    void input_iarray(char* s,int* a,int n,int imax);
    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N 1000000100
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N);
        fgets(s,N,stdin);
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int d=readint(s+i);
        free(s);
        //printf("%d %d\n",n,d);
        char* s2=(char*)malloc(sizeof(char)*N);
        fgets(s2,N,stdin);
        int* a=(int*)malloc(sizeof(int)*n);
        input_iarray(s,a,n,N);
        for(int i=1;i<n;i++){
            if((*(a+i)-*(a+i-1))<=d){
                printf("%d\n",*(a+i));
                exit(0);
            }
        }
        printf("%d\n",-1);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    void input_iarray(char* s,int* a,int n,int imax){
        int aidx=0,i=0; 
        while(*(s+i)!=0 && i<imax){
            if (*(s+i)<48 || *(s+i)>57){i++;}
            else{
                int bidx=i;
                while(*(s+i)>=48 && *(s+i)<=57){
                    i++;
                }
                int t=0;
                for(int j=bidx;j<i;j++){
                    t*=10;
                    t+=*(s+j)-48;
                }
            *(a+aidx++)=t;
            }
        }
        if (aidx!=n)printf("n!=aidx\n");
    }
        

    Rubyだと61秒かかっていたものがCでは1秒と、猛烈に速い。

    ところで、このコードをCで試すとSegmentation fault (core dumped)というエラーになり、原因が分からずStackoverflowで質問した。その際に、n,dの入力部分は省略したコードを載せたのだが、これは質問のしかたとしてはよくなかったようで、批判を頂いた。結局、原因はn,dの入力用に確保したメモリsでそのまま配列の入った文字列を読み込んだところにあったようで、そこを別にメモリを確保しなおしたら正常に動いた。


    ABC299-B

    問題概要

    整数n,tと二つの整数列が与えられる。配列cにtが一個以上含まれていれば、c[i]==tとなるr[i]のうちで最大のもののインデックス(1-indexed)を、tが配列cに含まれていなければ、c[i]==c[1]となるr[i]のうちで最大のもののインデックス(1-indexed)を出力せよ。

    解答

    n,t=gets.chomp.split(" ").map(&:to_i)
    c=gets.chomp.split(" ").map(&:to_i)
    r=gets.chomp.split(" ").map(&:to_i)
    cnt=0
    idxs1=[]
    idxs2=[]
    max1=0
    max2=0
    m1_idx=0
    m2_idx=0
    n.times do |i|
        if c[i]==t
            cnt+=1
            idxs1.push(i)
            if r[i]>max1
                max1=r[i]
                m1_idx=i
            end
        elsif c[i]==c[0]
            idxs2.push(i)
            if r[i]>max2
                max2=r[i]
                m2_idx=i
            end
        end
    end
    if cnt>0
        puts m1_idx+1
    else
        puts m2_idx+1
    end
        

    上が本番で提出したコード。以下はCで書いたもの。

    typedef struct {
        int c;
        int r;
    } player;
    void print_player(player *pl, int n);
    void print_ivector(int* a,int n);
    void input_iarray(char* s,int* a,int n,int imax);
    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 20
    #define N 100000002
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        fgets(s1,N1,stdin);
        int i=0;
        int n=readint(s1);
        while(*(s1+i)!=32)i++;
        i++;
        int t=readint(s1+i);
        free(s1);
        char* s2=(char*)malloc(sizeof(char)*N);
        fgets(s2,N,stdin);
        char* s3=(char*)malloc(sizeof(char)*N);
        fgets(s3,N,stdin);
        int * c=(int*)malloc(sizeof(int)*n);
        int * r=(int*)malloc(sizeof(int)*n);
        input_iarray(s2,c,n,N);
        input_iarray(s3,r,n,N);
    //    print_ivector(c,n);
    //    print_ivector(r,n);
        player* pls=(player*)(malloc(sizeof(player)*n));
        for(int i=0;i<n;i++){
            (pls+i)->c=*(c+i);
            (pls+i)->r=*(r+i);
        }
    //    print_player(pls,n);
        int max1=-1,max2=pls->r;
        int max1_idx=-1,max2_idx=0;
        int pl1c=pls->c;
        for(int i=0;i<n;i++){
            if ((pls+i)->c == t && (pls+i)->r > max1){
                max1=(pls+i)->r;
                max1_idx=i;
            }
            if ((pls+i)->c == pl1c  && (pls+i)->r > max2){
                max2=(pls+i)->r;
                max2_idx=i;
            }
        }
        if (max1==-1)printf("%d\n",max2_idx+1);
        else printf("%d\n",max1_idx+1);
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    void input_iarray(char* s,int* a,int n,int imax){
        int aidx=0,i=0; 
        while(*(s+i)!=0 && i<imax){
            if (*(s+i)<48 || *(s+i)>57){i++;}
            else{
                int bidx=i;
                while(*(s+i)>=48 && *(s+i)<=57){
                    i++;
                }
                int t=0;
                for(int j=bidx;j<i;j++){
                    t*=10;
                    t+=*(s+j)-48;
                }
            *(a+aidx++)=t;
            }
        }
        if (aidx!=n)printf("n!=aidx\n");
    }
    void print_ivector(int* a,int n){
        for(int i=0;i<n-1;i++){
            printf("%d ",*(a+i));
        }
        printf("%d\n",*(a+n-1));
    }
    void print_player(player *pl, int n){
        for(int i=0;i<n;i++)printf("[%d %d], ",(pl+i)->c,(pl+i)->r);
        printf("\n");
    }
        

    ABC300-A

    問題概要

    与えられた整数列のなかに、和がa+bであるものがあるか判定せよ。

    解答

    n,a,b=gets.chomp.split(" ").map(&:to_i)
    c=gets.chomp.split(" ").map(&:to_i)
    idx=0
    n.times do |i|
        if c[i]==(a+b)
            idx=i
            break
        end
    end
    puts idx+1
        

    上が本番で提出したコード。以下はCで書いたもの。

    void input_iarray(char* s,int* a,int n,int imax);
    int str_toi(char* s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 13
    #define N 1000000002
    int main(void){
        int n,a,b;
        int i=0;
        char *s=(char*)malloc(sizeof(char)*N1);
        fgets(s,N1,stdin);
    //    printf("%s\n",s);
        n=str_toi(s);
        while(*(s+i)!=32)i++;
        i++;
        a=str_toi(s+i);
        while(*(s+i)!=32)i++;
        i++;
        b=str_toi(s+i);
    //    printf("%d %d %d\n",n,a,b);
        char *s2=(char*)malloc(sizeof(char)*N);
        fgets(s2,N,stdin);
        int* c=(int*)malloc(sizeof(int)*n);
        input_iarray(s2,c,n,N);
    /*    for(int j=0;j<n;j++){
            printf("%d ",*(c+j));
        }
        printf("\n");*/
        for(int j=0;j<n;j++){
            if(*(c+j)==(a+b)){
                printf("%d\n",j+1);
                exit(0);
            }
        }
        printf("No\n");
        return 0;
    }
    int str_toi(char* s){
        int t=0,i=0,minus_flag=0;
        while((*(s+i)<48 || *(s+i)>57) && *(s+i)!='-' && *(s+i)!='+')i++;
        if (*(s+i)=='-'){
            minus_flag=1; i++;
        }else if (*(s+i)=='+'){
            i++;
        }
        while(*(s+i)>=48 && *(s+i)<=57){
            t*=10;
            t+=*(s+i)-48;i++;
        }
        if (minus_flag)t*=(-1);
        //printf("%s: %d\n",s,t);
        return t;
    }
    void input_iarray(char* s,int* a,int n,int imax){
        int aidx=0,i=0; 
        while(*(s+i)!=0 && i<imax){
            if (*(s+i)<48 || *(s+i)>57){i++;}
            else{
                int bidx=i;
                while(*(s+i)>=48 && *(s+i)<=57){
                    i++;
                }
                int t=0;
                for(int j=bidx;j<i;j++){
                    t*=10;
                    t+=*(s+j)-48;
                }
            *(a+aidx++)=t;
            }
        }
        if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
    }
        

    連続する整数列の中の抜けている数字を見つける

    ABC317-B

    問題概要

    連続する整数列の中の一つの要素が抜けた数列が与えられる。その抜けている要素を出力せよ。

    解答

    n=gets.to_i
    a=gets.chomp.split(" ").map(&:to_i)
    min=a.min
    max=a.max
    (min..max).each do |it|
        if !a.include?(it)
            puts it
        end
    end
        

    答えは一意に定まるという制約があるので、与えられた配列の両端に抜けている数字が来ることはない。なので、与えられた配列の最大値と最小値の間に抜けている数がないか探せばよい。

    上が本番で提出したコード。以下はCで書いたもの。なお、Cでのコードを書いたときに、毎回ループを回すのは無駄なので、最初に配列をソートしてしまった方がいいと気付いたが、ソートのコードをすぐには書けなかったので、やり方はとりあえずrubyでやったときと同じにした。

    void input_iarray(char* s,int* a,int n,int imax);
    int readint(char *s);
    #include<stdio.h>
    #include<limits.h>
    #include<stdlib.h>
    #define N1 20
    #define N 100000002
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        fgets(s1,N1,stdin);
        int n=readint(s1);
        char* s3=(char*)malloc(sizeof(char)*N);
        fgets(s3,N,stdin);
        int * a=(int*)malloc(sizeof(int)*n);
        input_iarray(s3,a,n,N);
        int max=0,min=INT_MAX;
        for(int i=0;i<n;i++){
            if (max<*(a+i))max=*(a+i);
            if (min>*(a+i))min=*(a+i);
        }
        for(int i=min+1;i<max;i++){
            int flag=0;
            for(int j=0;j<n;j++){
                if (*(a+j)==i)flag=1;
            }
            if (flag==0){
                printf("%d\n",i);
                exit(0);
            }
        }
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    void input_iarray(char* s,int* a,int n,int imax){
        int aidx=0,i=0; 
        while(*(s+i)!=0 && i<imax){
            if (*(s+i)<48 || *(s+i)>57){i++;}
            else{
                int bidx=i;
                while(*(s+i)>=48 && *(s+i)<=57){
                    i++;
                }
                int t=0;
                for(int j=bidx;j<i;j++){
                    t*=10;
                    t+=*(s+j)-48;
                }
            *(a+aidx++)=t;
            }
        }
        if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
    }
        

    二番目に大きい値

    ABC329-B

    問題概要

    整数を要素とする配列の中で、最大値ではない数のうち最大のものを出力せよ。なお、配列の全てが同じ数であることはない。

    解答

    制約により配列の要素の全てが同じ数である可能性はないので、uniqメソッドで配列の重複要素を削除すれば要素数は必ず2個以上になる。それを(昇順で)ソートして、後ろから2番目のものを出力すればよい。

    n=gets.to_i
    a=gets.chomp.split(" ").map(&:to_i)
    a.uniq!
    a.sort!
    puts a[a.size-2]
        

    上が本番で提出したコード。以下はCで書いたもの。

    typedef struct {
        int v;
        struct tn *l;
        struct tn *r;
    } tn;
    int push_element(tn* f, int* ar);
    tn* create_tn(int v);
    void insert_tn(int v, tn *node);
    int seccond_largest(tn* baum, int prev);
    void input_iarray(char* s,int* a,int n,int imax);
    int readint(char *s);
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #define N1 20
    #define N 100000002
    tn *root=NULL;
    int cnt=0, ar_idx=0;
    int main(void){
        char* s1=(char*)malloc(sizeof(char)*N1);
        fgets(s1,N1,stdin);
        int n=readint(s1);
        char* s3=(char*)malloc(sizeof(char)*N);
        fgets(s3,N,stdin);
        int * a=(int*)malloc(sizeof(int)*n);
        input_iarray(s3,a,n,N);
        for(int i=0;i<n;i++){
            insert_tn(*(a+i),root);
        }
        int * b=(int*)malloc(sizeof(int)*cnt);
        push_element(root,b);
        printf("%d\n",*(b+cnt-2));    
        return 0;
    }
    int readint(char *s){
        int i=0,ret=0;
        while(*(s+i)>=48 && *(s+i)<=57){
    //        printf("%d\n",*(s+i));
            ret*=10;
            ret+=*(s+i)-48;
            i+=1;
        }
        return ret;
    }
    void input_iarray(char* s,int* a,int n,int imax){
        int aidx=0,i=0; 
        while(*(s+i)!=0 && i<imax){
            if (*(s+i)<48 || *(s+i)>57){i++;}
            else{
                int bidx=i;
                while(*(s+i)>=48 && *(s+i)<=57){
                    i++;
                }
                int t=0;
                for(int j=bidx;j<i;j++){
                    t*=10;
                    t+=*(s+j)-48;
                }
            *(a+aidx++)=t;
            }
        }
        if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
    }
    tn* create_tn(int v){
        tn* neu;
        neu=(tn*)malloc(sizeof(tn));
        if(neu==NULL){
            printf("Memory allocation failure.");
            exit(0);
        }
        neu->l=NULL;
        neu->r=NULL;
        neu->v=v;cnt++;
        return neu;
    }
    void insert_tn(int v, tn *node){
        if(node==NULL){
            root=create_tn(v);
            return;
        } else if (v == node->v){
            return;
        } else if (v < node->v){
            if (node->l !=NULL){
                insert_tn(v,node->l);
            } else {
                node->l = create_tn(v);
            }
        } else if (node->v < v){
            if (node->r !=NULL){
                insert_tn(v,node->r);
            } else {
                node->r = create_tn(v);
            }
        }
        return;
    }
    int push_element(tn* f, int* ar){
        if(f->l!=NULL){
            push_element(f->l,ar);
        }
        *(ar+(ar_idx++))=f->v;
        if(f->r!=NULL){
            push_element(f->r,ar);
        }
    } 
        

    二分木の実装自体は書籍に載っていたコードを見て書いたので問題なかったが、二番目に大きい値を出力する部分でかなり手間取る。初めは、木から直接に二番目に大きい値が入っているノードを求めるコードを書こうとしたが、うまくいかないので、結局、一旦木の内容を配列に出力し、その最後から二番目の要素を出力することにした。いずれにせよ、こちらの方が汎用性のあるコードではある。