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;
}
    

0 件のコメント:

コメントを投稿