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

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

2022年11月24日木曜日

配列の要素の和

配列の和を求める

ABC272-A

問題概要

配列aが与えられたとき、aの要素の総和を求めよ。

解答

解答1

Arrayクラスにsumメソッドが用意されているので、それを使えばよい。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
puts a.sum
    

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

解答2

Arrayクラスのinjectメソッドを使う。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
puts a.inject {|result, item| result + item }
    

自分で、要素を一個ずつ足す処理を書いていってもいい。

解答3

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
sum=0
a.size.times do |i|
    sum+=a[i]
end
puts sum
    

解答4

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
sum=0
a.each do |item|
    sum+=item
end
puts sum
    

実行時間は、

実行時間(ms)
解答169
解答259
解答359
解答458

となった。

Cでやったのは以下。実行時間は 1 ms。

int isum(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 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);
    printf("%d\n",isum(a,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;
}
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);
}
int isum(int* a, int n){
    int sum=0;
    for(int i=0;i<n;i++)sum+= *(a+i);
    return sum;
}
    

配列の一部の和(インデックスを指定)

ABC290-A

問題概要

配列aの、b[1]..b[m]番目の和を求めよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
b=gets.chomp.split(" ").map(&:to_i)
ans=0
b.each do |it|
    ans+=a[it-1]
end
puts ans
    

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

int selected_isum(int* a, int* idxs,int n,int m);
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 n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * b=(int*)malloc(sizeof(int)*m);
    input_iarray(s3,b,m,N);
    free(s3);
    printf("%d\n",selected_isum(a,b,n,m));
    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);
}
int selected_isum(int* a, int* idxs,int n,int m){
    int sum=0;
    for(int i=0;i<m;i++){
        int idx=*(idxs+i)-1;
        sum+= *(a+idx);
    }
    return sum;
}
    

群ごとの和

ABC307-A

問題概要

与えられた配列を7個ごとの群に区切り、各群の数の和を順に出力せよ。

解答

nn=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
n=nn*7
r=n%7
if r>0
    b_size=(n/7+1)
else
    b_size=(n/7)
end
b=Array.new(b_size,0)
(n/7).times do |i|
    7.times do |j|
        b[i]+=a[i*7+j]
    end
end
if r>0
    r.times do |j|
        b[i+1]+=a[(n/7)*7+j]
    end
end
puts b.join(" ")
    

最初、nは与えられる配列の要素数だと思って書き進めたのだが、そうではなく、群の数だった。更に、7で割って余りが生じるかどうかの場合分けも不要であった。

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

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 n=readint(s1);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int* a=(int*)malloc(sizeof(int)*n*7);
    input_iarray(s2,a,n*7,N);
    free(s2);
    int* weekly=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)*(weekly+i)=0;
    int j=0;
    int wn=0;
    while(j<7*n){
        *(weekly+wn)+= *(a+7*wn+j%7);
        if (j%7==6)wn++;
        j++;
    }
    print_ivector(weekly,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;
}
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);
}
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));
}
    

条件を満たす要素の和

ABC328-B

問題概要

整数からなる配列が与えられる。値が一定値以下である要素の和を出力せよ。

解答

n,x=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
a=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
ans=0
(0...n).each do |i|
    if a[i]&lt;=x
        ans+=a[i]
    end
end
puts ans
    

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

int if_smaller(int* np,int x);
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 n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int x=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    int ans=0;
    for(int i=0;i<n;i++)ans+= (if_smaller(a+i,x));
    printf("%d\n",ans);
    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);
}
int if_smaller(int* np,int x){
    if (*np<=x)return *np;
    else return 0;
}
    

2022年11月13日日曜日

要素の数を数える問題・正の字カウント・ハッシュ

数を数える

今回は、具体的なABCの問題から入るのではなく、一般的な場合から。

与えられたデータの中に、区別できるものがそれぞれ何個ずつあるかを数える。。

まずは、要素の種類が分かっている場合

例えば、o,xだけとか、"Yes", "No"だけとか、一桁の数字だけとか。

もっと多くて、都道府県とか、アメリカ合衆国の州とかいう場合も考えられる。

こういう時には、あらかじめハッシュテーブルを用意しておいて、ハッシュのキーと同じものが出てきたら、そのキーのの値を一つずつ増やしていけばよい。

設例1

データ oxooxoxxxxoo が標準入力から与えられるとする。この中に、o と x はそれぞれ何個ずつあるか。

解答

h={o:0,x:1}
a=gets.chomp.split("").to_a
a.each do |it|
    h[it.to_sym]+=1
end
puts "o:#{h[:o]}"
puts "x:#{h[:x]}"    
    

実行結果は、

o:3
x:3
    

次に、出現する要素があらかじめわかっていない場合どうするかを見ていく。

あらかじめわかっていない要素の数を数える問題

[a,a,a,b,b,c]のような、特異な要素がそれぞれ複数個ありうる配列が与えられて、その特異な要素ごとにその数を数える、というタイプの問題である。実用度はかなり高い。手作業でやる時には、「順番に見ていって、新しい要素が出てきたらその名前を書いて、その下に正の字の最初の一画を書き、二回以上出現した要素については、既に書いた名前の下の正の字の画数を増やしていく」、ということをするのだが、それをコンピューターでやろうとするとどうするか。

データ

μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος
οὐλομένην, ἣ μυρί᾽ Ἀχαιοῖς ἄλγε᾽ ἔθηκε,
πολλὰς δ᾽ ἰφθίμους ψυχὰς Ἄϊδι προΐαψεν
ἡρώων, αὐτοὺς δὲ ἑλώρια τεῦχε κύνεσσιν
5οἰωνοῖσί τε πᾶσι, Διὸς δ᾽ ἐτελείετο βουλή,
ἐξ οὗ δὴ τὰ πρῶτα διαστήτην ἐρίσαντε
Ἀτρεΐδης τε ἄναξ ἀνδρῶν καὶ δῖος Ἀχιλλεύς.
τίς τ᾽ ἄρ σφωε θεῶν ἔριδι ξυνέηκε μάχεσθαι;
の中に出てくる文字を文字ごとに集計せよ。(アクセント記号、気息記号が異なれば別の文字として扱う。)

解答

一つの案は、配列を使うやり方で、

s=<<'EOS'
μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος
οὐλομένην, ἣ μυρί᾽ Ἀχαιοῖς ἄλγε᾽ ἔθηκε,
πολλὰς δ᾽ ἰφθίμους ψυχὰς Ἄϊδι προΐαψεν
ἡρώων, αὐτοὺς δὲ ἑλώρια τεῦχε κύνεσσιν
5οἰωνοῖσί τε πᾶσι, Διὸς δ᾽ ἐτελείετο βουλή,
ἐξ οὗ δὴ τὰ πρῶτα διαστήτην ἐρίσαντε
Ἀτρεΐδης τε ἄναξ ἀνδρῶν καὶ δῖος Ἀχιλλεύς.
τίς τ᾽ ἄρ σφωε θεῶν ἔριδι ξυνέηκε μάχεσθαι;
EOS
list=[]
number=[]
(0...s.size).each do |i|
    if s[i]=="\n" || s[i]==" "
        next
    elsif list.include?(s[i])
        number[list.index(s[i])]+=1
    else
        list.push(s[i])
        number.push(1)
    end
end
list.size.times do |i|
    puts "#{list[i]} : #{number[i]}"
end
    

実行結果は、

μ : 5
ῆ : 2
ν : 16
ι : 14
ἄ : 4
ε : 22
δ : 12
θ : 5
ὰ : 4
Π : 1
η : 7
λ : 11
ϊ : 2
ά : 2
ω : 4
Ἀ : 4
χ : 6
ο : 14
ς : 11
ὐ : 2
έ : 2
, : 5
ἣ : 1
υ : 5
ρ : 10
ί : 6
᾽ : 5
α : 10
ῖ : 3
γ : 1
ἔ : 2
κ : 4
π : 4
ἰ : 2
φ : 2
ψ : 2
Ἄ : 1
ΐ : 2
ἡ : 1
ώ : 2
τ : 14
ὺ : 1
ὲ : 1
ἑ : 1
ῦ : 1
ύ : 2
σ : 8
5 : 1
ᾶ : 1
Δ : 1
ὸ : 1
ἐ : 3
β : 1
ή : 2
ξ : 3
ὗ : 1
ὴ : 1
ῶ : 3
ἀ : 1
ὶ : 1
. : 1
; : 1
のようになった。

もう一つのやり方として、rubyにはハッシュという便利なクラスが用意されているので、それを使うもの。実際に問題を解く際にはたいていこちらでやっているので、これについてはそちらの実例を参照。


数を数える

ABC240-B

問題概要

長さ N の正整数列 aには何種類の整数が現れるか。

解答

n=gets.to_i
a=gets.split.map(&:to_i)
b=[]
c=[]
n.times do |i|
    if b.include?(a[i])
        c[b.index(a[i])]+=1
    else
        b.push(a[i])
        c[b.index(a[i])]=1
    end
end
puts b.size
    

上が本番で提出したコード。Cで最初にやったのが以下。(REになった。)

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 2e9
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    int max=0;
    for(int i=0;i<n;i++){
        if (*(a+i)>max)max=*(a+i);
    }
    int * nums=(int*)malloc(sizeof(int)*(max+1));
    for(int i=0;i<=max;i++)*(nums+i)=0;
    for(int i=0;i<n;i++){
        int num=*(a+i);
        *(nums+num)+=1;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if (*(nums+i))ans++;
    }
    printf("%d\n",ans);
    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);
}
    

サンプル3件ではACになるが、他のテストケースでは全てREになった。

Paiza.ioにテストケースのデータを入れてやってみると、こちらでもエラーになる。一旦コード全体をコメントアウトし、少しずつコメントアウトを外しながらエラーが起る場所を探してみると、問題の箇所は25行目(*(nums+num)+=1;)のようだ。

だが、メモリは十分に確保しているはずなのに、どうしてエラーになるのか分からず、StackOverflowで質問。

原因は、メモリ制限がPaiza.ioで512MB, AtCoderでも1024MBなのに対し、制約上のa[i]の最大値10^9個のint型メモリを確保しようとすると4000MBになってしまうのが原因のようだ。それで、mallocがNULLを返すが、そこにそのままデータを入れようとしてランタイムエラーになったらしい。(attakeiさん、actorbugさん、コメント・回答有難うございました。)

それで、結局二分木を使って出現した数のリストを作ることに。と言っても、少し前に作ったABC329-Bのコードがほぼそのまま使えたので、新しく書いたところはほとんどない。それが以下。

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",cnt);    
    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);
    }
} 
    

ABC263-A

問題概要

フルハウスかどうか判定(正の字カウント)

ABC263-A

問題概要

5つの数がが与えられる。それらが、それぞれ3個と2個の二種類の数字からなるかどうか判定せよ。

解答

本番では別のやり方でやったのだが、これは正の字カウントでやった方が素直だし早い。

a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
ss=h.size
if ss!=2
    puts "No"
    exit
end
hv=h.values
if hv[0]==2 && hv[1]==3 || hv[0]==3 && hv[1]==2
    puts "Yes"
else
    puts "No"
end
    

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 22
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=(int*)malloc(sizeof(int)*5);
    if (a==NULL){
        printf("memory allocation to a failed.\n");
        exit(1);
    }
    int i=0,j=0;
    for(;i<4;i++){
        *(a+i)=readint(s1+j);
        while(*(s1+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s1+j);
    int* r=(int*)malloc(sizeof(int)*14);
    if (a==NULL){
        printf("memory allocation to r failed.\n");
        exit(1);
    }
    for(i=0;i<=13;i++)*(r+i)=0;
    for(i=0;i<5;i++){
        int t=*(a+i);
        *(r+t)+=1;
    }
    int cnt=0;
    for(i=1;i<=13;i++){
        if(*(r+i))cnt++;
    }
//    printf("%d\n",cnt);
    if (cnt!=2){
        printf("No\n");
        exit(0);
    }else{
        int n1=0,n2=0;
        for(i=1;i<=13;i++){
            if(*(r+i)){
                if (n1==0)n1=*(r+i);
                else n2=*(r+i);
            }
        }
//        printf("n1=%d, n2=%d\n",n1,n2);
        if ((n1==3 && n2==2) || n2==3 && n1==2){
            printf("Yes\n");
            exit(0);
        }else{
            printf("No\n");
            exit(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;
}
    

昨日やったABC268-Aのコードがかなり流用できたので、今回はmallocのNULLチェックも入れてみた。


ABC268-A

問題概要

5個の数字が与えられる。この中に数字が何種類あるか答えよ。

解答

a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
puts h.size
    

空ハッシュを作っておき、配列の要素をはじめから順番に見ていって、もしハッシュのキーにその要素が既にあれば、その値を一増やし、もしなければ新しいキーを追加し、その値を1にしている。問題の答えは、最終的なハッシュのサイズになる。

もっとも、この問題の場合、各キーの個数は必要なく、ユニークな要素が何種類あるか分かればそれでいいから、

a=gets.chomp.split(" ").map(&:to_i)
au=a.uniq!
puts au.size
    

としてもよい。

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 22
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int* a=(int*)malloc(sizeof(int)*5);
    int i=0,j=0;
    for(;i<4;i++){
        *(a+i)=readint(s1+j);
        while(*(s1+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s1+j);
    int* r=(int*)malloc(sizeof(int)*101);
    for(i=0;i<=100;i++)*(r+i)=0;
    for(i=0;i<5;i++){
        int t=*(a+i);
        *(r+t)+=1;
    }
    int ans=0;
    for(i=0;i<=100;i++){
        if(*(r+i))ans++;
    }
    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;
}
    

数を数える

ABC287-A

問題概要

ForまたはAgainstからなる文字列がn個与えられる。Forが過半数より多いか否か判定せよ。

解答

n=gets.to_i
count=0
n.times do |i|
    s=gets.chomp
    if s=="For"
        count+=1
    end
end
if count>n/2
    puts "Yes"
else
    puts "No"
end
        

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

int scmp(char *s, char *t);
int is_nonletter(char *c);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N2 1000
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    char* s=(char*)malloc(sizeof(char)*N2);
    for(int i=0;i<n;i++){
        fgets(s+10*i,10,stdin);
    }
/*    for(int i=0;i<n;i++){
        printf("%s\n",s+10*i);
    }*/
    const char* ar[2]={"For","Against"};
//    printf("%s\n",ar[0]);
    int yes=0,no=0;
    for(int i=0;i<n;i++){
        if(scmp(s+10*i,ar[0])==0)yes++;
        else if(scmp(s+10*i,ar[1])==0)no++;
    }
    if (yes>no){
        printf("Yes");
    }else{
        printf("No");
    }
    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;
}
int scmp(char *s, char *t){
    int i=0;
    //printf("s=%s,t=%s\n",s,t); 
    int flag=4;
    while(1){
        if (is_nonletter(s+i) && is_nonletter(t+i))flag=0;
        else if (is_nonletter(s+i) && !is_nonletter(t+i))flag=-2;
        else if (!is_nonletter(s+i) && is_nonletter(t+i))flag=2;
        else if(*(s+i)>*(t+i))flag=1;
        else if(*(s+i)<*(t+i))flag=-1;
        else if(*(s+i)==*(t+i))i++;
        else flag=-2;
        if (flag!=4)return flag;
    }
    return flag;
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}
        

条件に一致するものの数を数える

ABC287-B

問題概要

数字文字列の配列s(文字数6),t(文字数3)がある。配列sに含まれる文字列のうち、その4~6文字目が配列tに含まれる文字列と一致するものがいくつあるか求めよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
s=[]
t=[]
count=0
(0...n).each do |i|
    s[i]=gets.chomp
end
(0...m).each do |i|
    t[i]=gets.chomp
end
(0...n).each do |i|
    (0...m).each do |j|
        if s[i].slice(3,3)==t[j]
            count+=1
            break
        end
    end
end
puts count
    

上が本番で提出したコード。Cで書いたのが以下だが、まずはWAになったコード。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define NS 8000
#define NT 5000
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*NS);
    char* s3=(char*)malloc(sizeof(char)*NT);
    for(int i=0;i<n;i++){
        fgets(s2+i*6,8,stdin);
    }
    for(int i=0;i<m;i++){
        fgets(s3+i*3,5,stdin);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int flag=1;
            for(int k=0;k<3;k++){
                if (*(s2+i*6+3+k)!=*(s3+j*3+k)){
                    flag=0;
                    break;
                }
                if (flag==0)break;
            }
            if (flag==1)ans++;
        }
    }
    printf("%d\n",ans);
    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;
}
    

WAになるテストケースを見てみると、tの要素に重複するものがある。つまり、この問題で問われているのは、「Sの要素のうち、tの要素のいずれかと一致するものがいくつあるか。」なので、s[i]がtの要素のうち2つ以上に一致しても、それは一回とか添えなければいけないのである。

すると、まずはtをユニークな配列に作り直さなければならないのか、これをCでやるとなると意外と面倒だなど思いつつ、まずはrubyで書いたときはどうしたか見てみることになる。すると、何か特別な処理をしているようには見えない。これは一体どういうことだ、としばらく悩んだ。(rubyでのコードを書いたのは一年近く前のことなので、その特に何を考えていたかなど覚えていない。)

しばらくして、ようやく、rubyで書いたコードでは、一致するものが見つかった時点でtの要素を回すループを抜けていることに気づく。こうすればよかったのが。意外と単純な解法があった。(なお、rubyではuniqという便利なメソッドがあるので、tを重複要素の無いものにすることも簡単にできる。)それでやり直してACになったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define NS 8000
#define NT 5000
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*NS);
    char* s3=(char*)malloc(sizeof(char)*NT);
    for(int i=0;i<n;i++){
        fgets(s2+i*6,8,stdin);
    }
    for(int i=0;i<m;i++){
        fgets(s3+i*3,5,stdin);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int flag=1;
            for(int k=0;k<3;k++){
                if (*(s2+i*6+3+k)!=*(s3+j*3+k)){
                    flag=0;
                    break;
                }
                if (flag==0)break;
            }
            if (flag==1){
                ans++;
                break;
            }
        }
    }
    printf("%d\n",ans);
    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;
}
    

列ごとに特定の値の要素を数える

ABC274-B

問題概要

要素が#または.である行列が与えられる。各列の#の数を順に出力せよ

解答

n,m=gets.chomp.split(" ").map(&:to_i)
a=[]
ans=Array.new(m,0)
n.times do |i|
    a[i]=gets.chomp.split("").to_a
    m.times do |j|
        if a[i][j]=="#"
            ans[j]+=1
        end
    end
end
puts ans.join(" ")
    

上が本番で提出したコード

他のやり方としては、転置行列を作って、各行の要素数を数えるというものも考えられる。そうすると、Arrayクラスのcountメソッドが使える。

n,m=gets.chomp.split(" ").map(&:to_i)
a=[]
b=Array.new(m){Array.new(n,"")}
n.times do |i|
    a[i]=gets.chomp.split("").to_a
    m.times do |j|
        b[j][i]=a[i][j]
    end
end
m.times do |i|
    print b[i].count("#")
    if i!=m-1
        print(" ")
    end
end
puts ""
    

ただ、最初のコードの実行時間が285msなのに対し、下のコード475msと、実行時間が約1.7倍に増えている。そのうえメモリ使用量も4%くらい増えているから、本番で敢えてこれを使うメリットはないだろう。

Cでやったのが以下。

void print_ivector(int* a,int n);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 1e6
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int h=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int w=readint(s+i);
    free(s);
    //printf("%d %d\n",h,w);
    char* s2=(char*)malloc(sizeof(char)*N+2);
    for(int j=0;j<h;j++){
        char* t=(char*)malloc(sizeof(char)*(w+2));
        fgets(t,w+2,stdin);
        for(int k=0;k<w;k++)*(s2+j*w+k)=*(t+k);
    }
    /*
    for(int j=0;j<h;j++){
        for(int k=0;k<w;k++){
            printf("%c",*(s2+j*w+k));
        }
        printf("\n");
    }
    */
    int* a=(int*)malloc(sizeof(int)*w);
    for(int j=0;j<w;j++){
        int temp=0;
        for(int k=0;k<h;k++){
            if (*(s2+k*w+j)==35)temp++;
        }
        *(a+j)=temp;
    }
    print_ivector(a,w);
    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 print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
    }
    

文字列中の文字の出現回数を数える

ABC279-A

問題概要

vとwからなる文字列が与えられる。下に尖った個所が何カ所あるかを求めよ。

解答

s=gets.chomp.split("").to_a
ans=0
s.size.times do |i|
    if s[i]=="v"
        ans+=1
    elsif s[i]=="w"
        ans+=2
    end
end
puts ans
    

上が本番の提出コード。一文字ずつ見ていって、vが出てきたら1、wが出てきたら2を答えに加算している。

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 102
int main(void){
    int ans=0;  
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    for(int n=0;n<N;n++){
        if (*(s+n)==118){
            ans+=1;
        }
        if (*(s+n)==119){
            ans+=2;
        }
    }
    printf("%d\n",ans);
    return 0;
}

行列中の特定の要素の数を数える。

ABC280-A

問題概要

.または#を要素とする行列が与えられる。#が何個あるか出力せよ。

解答

h,w=gets.chomp.split(" ").map(&:to_i)
ans=0
h.times do |i|
    t=gets.chomp.split("").to_a
    c=0
    w.times do |j|
        if t[j]=="#"
            c+=1
        end
    end
    ans+=c
end
puts ans
    

上が本番の提出コード。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 7
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N);
    fgets(s1,N,stdin);
    int i=0;
    int h=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int w=readint(s1+i);
    free(s1);
//    printf("%d %d\n",h,w);
    int ans=0;
    for(i=0;i<h;i++){
        char* s2=(char*)malloc(sizeof(char)*(w+2));
        fgets(s2,(w+2),stdin);
        for(int j=0;j<w;j++){
//            printf("%c",*(s2+j));
            if(*(s2+j)==35)ans++;
        }
//        printf("\n");
    }
    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;
}
    

ABC295-C

問題概要

数字を要素とする配列が与えられる。同じ数字のペアは何個作れるか。

解答

「正の字カウント」を使えばよい。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
count=0
h.each_value do |v|
    count+=v/2
end
puts count
    

上が本番の提出コードCでやったのが以下。まずは、以下のコードでTLEになった。

typedef struct {
    int key;
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
tn* create_tn(int key);
void insert_tn(int key, tn *node);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 8
#define N 1e7
tn *root=NULL;
int cnt=0,ar_idx=0;
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 n=readint(s1);
    free(s1);
    char* s=(char*)malloc(sizeof(char)*N);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    fgets(s,N,stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    if (a==NULL){
        printf("memory allocation to a failed.\n");
        exit(1);
    }
    int i=0,j=0;
    for(;i<n-1;i++){
        *(a+i)=readint(s+j);
        while(*(s+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s+j);
    free(s);
//    print_ivector(a,n);
    for(int i=0;i<n;i++){
        insert_tn(*(a+i),root);
    }
    free(a);
    int *a1=(int*)malloc(sizeof(int)*cnt);
    if (a1==NULL){
        printf("memory allocation to a1 failed.\n");
        exit(1);
    }
    int *a2=(int*)malloc(sizeof(int)*cnt);
    if (a2==NULL){
        printf("memory allocation to a2 failed.\n");
        exit(1);
    }
    tree_to_pa(root,a1,a2);
    int ans=0;
    for(i=0;i<cnt;i++){
        ans+= *(a2+i)/2;
    }
//    print_ivector(a1,cnt);
//    print_ivector(a2,cnt);
    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;
}
tn* create_tn(int key){
    tn* neu;
    neu=(tn*)malloc(sizeof(tn));
    if(neu==NULL){
        printf("Memory allocation failed.");
        exit(1);
    }
    neu->l=NULL;
    neu->r=NULL;
    neu->key=key;cnt++;
    neu->v=1;
    return neu;
}
void insert_tn(int key, tn *node){
    if(node==NULL){
        root=create_tn(key);
        return;
    } else if (key == node->key){
        (node->v)+=1;
        return;
    } else if (key < node->key){
        if (node->l !=NULL){
            insert_tn(key,node->l);
        } else {
            node->l = create_tn(key);
        }
    } else if (node->key < key){
        if (node->r !=NULL){
            insert_tn(key,node->r);
        } else {
            node->r = create_tn(key);
        }
    }
    return;
}
int tree_to_pa(tn* f, int* a1, int* a2){
    if(f->l!=NULL){
        tree_to_pa(f->l,a1,a2);
    }
    *(a1+(ar_idx))=f->key;
    *(a2+(ar_idx))=f->v;ar_idx++;
    if(f->r!=NULL){
        tree_to_pa(f->r,a1,a2);
    }
} 
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));
}
    

少し前にABC329-Bで二分木を使うコードを書いたので、それを少し修正して、二分木の葉にキーと値の二つを持たせ、数字をキーとするハッシュとして使えるようにした。

AtCoderでの実行結果の詳細を見ると、sortedという名の付いた最後4件のデータでTLEになっている。どうやら、二分木にソート済みのデータを入れると枝が一方向に伸び、探索にO(n)の時間がかかるというあの現象が起きているようだ。


決まった要素の出現回数、+同数の場合の処理

ABC301-A

問題概要

AとTからなる文字列が与えられる。AとTのどちらが多いか。ただし、同数の場合、先にその数に達した方を答えよ。

解答

出現する要素の種類が分かっているので、その要素をキーとするハッシュの値を加算していけばよい。同数の場合については、「文字列の最後の要素でない方」を答えればよい。

n=gets.to_i
s=gets.chomp
count=Hash.new
count["A"]=0
count["T"]=0
n.times do |i|
    count[s[i]]+=1
end
if count["A"]>count["T"]
    puts "A"
elsif count["T"]>count["A"]
    puts "T"
else
    if s[-1]=="T"
        puts "A"
    else
        puts "T"
    end
end
    

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

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 102
int main(void){
    int n,na=0,nt=0;
    char *sn=(char*)malloc(sizeof(char)*5);
    fgets(sn,5,stdin);
    n=readint(sn);
//    printf("%d\n",n);
    char *s=(char*)malloc(sizeof(char)*N1);
    fgets(s,N1,stdin);
//    printf("%s\n",s);
    for(int i=0;i<n;i++){
        if (*(s+i)==65)na++;
        else if (*(s+i)==84)nt++;
    }
    if (na>nt)printf("A\n");
    else if (na<nt)printf("T\n");
    else if (na==nt){
        if (*(s+n-1)==65)printf("T\n");
        else printf("A\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;
}
    

ABC308-B

問題概要

文字列を要素とする配列が与えられる。その各要素は品目名であり、品目名と価格を対応させた表が与えられる。ただし価格表に載っていない品目もあり、その価格はデフォルト値となる。価格の合計を出力せよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
c=gets.chomp.split(" ")
d=gets.chomp.split(" ")
p=gets.chomp.split(" ").map(&:to_i)
ans=0
n.times do |i|
    if !d.include?(c[i])
        k=p[0]
    else
        k=p[d.find_index(c[i])+1]
    end
    ans+=k
end
puts ans
    

Cでまず書いたのが以下。このコードは、sample1だけWAになった。

typedef struct {
    int id;
    char key[21];
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_tn(tn* tns, int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 9
#define N2 2102
#define N 100000002
tn* create_tn(int key);
void insert_tn(int key, tn *node);
tn *root=NULL;
int cnt=0,ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("Memory allocation to s1 failed.");
        exit(1);
    }
    fgets(s1,N1,stdin);
    int i=0;
    int n=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int m=readint(s1+i);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*(21*n+1));
    if(s2==NULL){
        printf("Memory allocation to s2 failed.");
        exit(1);
    }
    fgets(s2,(21*n+1),stdin);
//    printf("s2=%s\n",s2);
    char* s3=(char*)malloc(sizeof(char)*(21*m+1));
    if(s3==NULL){
        printf("Memory allocation to s3 failed.");
        exit(1);
    }
    fgets(s3,(21*m+1),stdin);
//    printf("s3=%s\n",s3);
    char* s4=(char*)malloc(sizeof(char)*(6*m+1));
    if(s4==NULL){
        printf("Memory allocation to s4 failed.");
        exit(1);
    }
    fgets(s4,6*(m+1)+1,stdin);
//    printf("s4=%s\n",s4);
    int* p=(int*)malloc(sizeof(int)*(m+1));
    if (p==NULL){
        printf("memory allocation to p failed.\n");
        exit(1);
    }
    input_iarray(s4,p,m+1,6*m+1);
//    print_ivector(p,m+1);
    free(s4);
    tn* arr=(tn*)malloc(sizeof(tn)*m);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s3_idx=0;
    i=0;
    for(;i<m;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s3+s3_idx=%s\n",s3+s3_idx);
        while(*(s3+s3_idx)!=32 && *(s3+s3_idx)!=0 && *(s3+s3_idx)!='\n'){
            *(temp+k)=*(s3+s3_idx); k++; s3_idx++;
        }
//        printf("temp(%d)=%s\n",i,temp);
        ((arr+cnt)->id)=cnt;
        strcpy((arr+cnt)->key,temp);
        ((arr+cnt)->v)=*(p+cnt+1);
        s3_idx++;
        cnt++; 
    }
//    print_tn(arr,m);
    int total=0;
    int s2_idx=0;
    for(i=0;i<n;i++){
        char* temp2=(char*)malloc(sizeof(char)*22);
        if (temp2==NULL){
            printf("memory allocation to temp2(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s2+s2_idx=%s\n",s2+s2_idx);
        while(*(s2+s2_idx)!=32 && *(s2+s2_idx)!=0 && *(s2+s2_idx)!='\n'){
            *(temp2+k)=*(s2+s2_idx); k++; s2_idx++;
        }
        s2_idx++;
//        printf("temp2(%d)=%s\n",i,temp2);
        int flag=0,price=0;
        for(int j=0;j<m;j++){
            if(strcmp(temp2,(arr+j)->key)==0){
                flag=1;
                price=(arr+j)->v;
                break;
            }
        }
        if(flag==1){
//            printf("%s: price=%d\n",temp2,price);
            total+=price;
        }else{
//            printf("%s: price=%d\n",temp2,*p);
            total+=*p;
        }
    }
    printf("%d\n",total);
    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;
}
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_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("id=%d\n",(tns+i)->id);
        printf("key=%s\n",(tns+i)->key);
        printf("price=%d\n",(tns+i)->v);
    }
}
    

AtCoderで提出するとsample1の一つだけWAになるのだが、sample1のデータを入れて実行すると、正しい答えが出ている。原因が分からないので、StackOverflowで質問した。

actorbugさんから頂いた回答によると、strcmpで比較するために文字列を入れたtemp,temp2の最後に\0を書き込んでいなかったのが原因らしい。(他にも幾つか修正すべき点を指摘していただいた。)

ちなみに、この比較の部分は、最初はCの標準関数のstrcmpを使うのではなく、自作関数で比較しようとしたのだが、構造体のメンバである文字列のアドレスを主とルする方法が分からず、strcmpを使うことにしたのだった。

指摘されたとおりに修正したら、全てACになった。そのコードが以下。

typedef struct {
    int id;
    char key[21];
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_tn(tn* tns, int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 9
#define N2 2102
#define N 100000002
tn* create_tn(int key);
void insert_tn(int key, tn *node);
tn *root=NULL;
int cnt=0,ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("Memory allocation to s1 failed.");
        exit(1);
    }
    fgets(s1,N1,stdin);
    int i=0;
    int n=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int m=readint(s1+i);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*(21*n+1));
    if(s2==NULL){
        printf("Memory allocation to s2 failed.");
        exit(1);
    }
    fgets(s2,(21*n+1),stdin);
//    printf("s2=%s\n",s2);
    char* s3=(char*)malloc(sizeof(char)*(21*m+1));
    if(s3==NULL){
        printf("Memory allocation to s3 failed.");
        exit(1);
    }
    fgets(s3,(21*m+1),stdin);
//    printf("s3=%s\n",s3);
    char* s4=(char*)malloc(sizeof(char)*(6*(m+1)+1));
    if(s4==NULL){
        printf("Memory allocation to s4 failed.");
        exit(1);
    }
    fgets(s4,6*(m+1)+1,stdin);
//    printf("s4=%s\n",s4);
    int* p=(int*)malloc(sizeof(int)*(m+1));
    if (p==NULL){
        printf("memory allocation to p failed.\n");
        exit(1);
    }
    input_iarray(s4,p,m+1,6*m+1);
//    print_ivector(p,m+1);
    free(s4);
    tn* arr=(tn*)malloc(sizeof(tn)*m);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s3_idx=0;
    i=0;
    for(;i<m;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s3+s3_idx=%s\n",s3+s3_idx);
        while(*(s3+s3_idx)!=32 && *(s3+s3_idx)!=0 && *(s3+s3_idx)!='\n'){
            *(temp+k)=*(s3+s3_idx); k++; s3_idx++;
        }
        *(temp+k)=0;
//        printf("temp(%d)=%s\n",i,temp);
        ((arr+cnt)->id)=cnt;
        strcpy((arr+cnt)->key,temp);
        free(temp);
        ((arr+cnt)->v)=*(p+cnt+1);
        s3_idx++;
        cnt++; 
    }
    free(s3);
//    print_tn(arr,m);
    int total=0;
    int s2_idx=0;
    for(i=0;i<n;i++){
        char* temp2=(char*)malloc(sizeof(char)*22);
        if (temp2==NULL){
            printf("memory allocation to temp2(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s2+s2_idx=%s\n",s2+s2_idx);
        while(*(s2+s2_idx)!=32 && *(s2+s2_idx)!=0 && *(s2+s2_idx)!='\n'){
            *(temp2+k)=*(s2+s2_idx); k++; s2_idx++;
        }
        *(temp2+k)=0;
        s2_idx++;
//        printf("temp2(%d)=%s\n",i,temp2);
        int flag=0,price=0;
        for(int j=0;j<m;j++){
            if(strcmp(temp2,(arr+j)->key)==0){
                flag=1;
                price=(arr+j)->v;
                break;
            }
        }
        free(temp2);
        if(flag==1){
//            printf("%s: price=%d\n",temp2,price);
            total+=price;
        }else{
//            printf("%s: price=%d\n",temp2,*p);
            total+=*p;
        }
    }
    free(s2);
    free(arr);
    free(p);
    printf("%d\n",total);
    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;
}
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_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("id=%d\n",(tns+i)->id);
        printf("key=%s\n",(tns+i)->key);
        printf("price=%d\n",(tns+i)->v);
    }
}
    

ハッシュ

ABC319-A

問題概要

文字列と数値の組の表がある。表中の文字列の一つが与えられるので、それに対応する数値を出力せよ。

解答

data=<<EOF
tourist 3858
ksun48 3679
Benq 3658
Um_nik 3648
apiad 3638
Stonefeang 3630
ecnerwala 3613
mnbvmar 3555
newbiedmy 3516
semiexp 3481tourist 3858
EOF
d=data.split("\n")
da=Array.new(d.size){Array.new(2)}
i=0
d.each do |it|
    s,n=it.split(" ")
    da[i][0]=s
    da[i][1]=n.to_i
    i+=1
end
s=gets.chomp
da.each do |dd|
    if dd[0]==s
        puts dd[1]
        exit
    end
end
    

上が本番の提出コード。Cでやったのが以下。

typedef struct {
    char key[21];
    int v;
} tn;
void print_tn(tn* tns, int n);
int readint(char *s);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 10
#define N2 12
#define N 100000002
int cnt=0,ar_idx=0;
int main(void){
    char s1[]="tourist 3858,ksun48 3679,Benq 3658,Um_nik 3648,apiad 3638,Stonefeang 3630,ecnerwala 3613,mnbvmar 3555,newbiedmy 3516,semiexp 3481";
//    printf("%s\n",s1);
    tn* arr=(tn*)malloc(sizeof(tn)*N1);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s1_idx=0,i=0;
    for(i=0;i<N1;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
        while(*(s1+s1_idx)!=32){
            *(temp+k)=*(s1+s1_idx); k++; s1_idx++;
        }
        *(temp+k)=0;
        strcpy((arr+i)->key,temp);
        free(temp);
        s1_idx++;
        int point=readint(s1+s1_idx);
        (arr+i)->v=point;
        if (i!=N1-1){
            while(*(s1+s1_idx)>=48 && *(s1+s1_idx)<=57){
                s1_idx++;
            }
            while(*(s1+s1_idx)==44)s1_idx++;
        }
    }
//    print_tn(arr,N1);
    char* s2=(char*)malloc(sizeof(char)*N2);
    if (s2==NULL){
        printf("memory allocation to s2 failed.\n");
        exit(1);
    }
    fgets(s2,N2,stdin);
    char* temp2=(char*)malloc(sizeof(char)*(N2+1));
    if (temp2==NULL){
        printf("memory allocation to temp2 failed.\n");
        exit(1);
    }
    int s2_idx=0,k=0;
    while((*(s2+s2_idx)>=48 && *(s2+s2_idx)<=122)){
        *(temp2+k)=*(s2+s2_idx); s2_idx++; k++;
    }
    *(temp2+k)=0;
//    printf("%s\n",temp2);
    int flag=0;
    for(i=0;i<N1;i++){
        if (strcmp((arr+i)->key,temp2)==0){
            printf("%d",(arr+i)->v);
            flag=1;
            exit(0);
        }
    }
    if (flag==0){
        printf("name couldn't be found.\n");
        exit(1);
    }
    free(arr);
    free(temp2);
    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;
}
void print_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("key=%s\n",(tns+i)->key);
        printf("point=%d\n",(tns+i)->v);
    }
}
    

ABC325-B

問題概要

拠点iの標準時は世界標準時+w[i]で、x[i]人が所属している。(w[i]は整数。)整数時から始まる1時間に各拠点の標準時における9時から18時が完全に含まれる拠点の所属員を累計するとき、その値の最大値を求めよ。

解答

n=gets.to_i
x=[]
w=[]
n.times do |i|
    x[i],w[i]=gets.chomp.split(" ").map(&:to_i)
end
h=Hash.new
(0..23).each do |i|
    t=0
    n.times do |j|
        st=i+w[j]
        if st>24
            st-=24
        end
        if st>=9 && st<=17
            t+=x[j]
        end
    end
    h[i]=t
end
max=0
h.each {|key, value| 
    if value > max
        max=value
    end
}
puts max
    

この問題では開始時間は整数時の24通りに限られるので、0..23時をそれぞれ開始時間としたときに開始時間の現地標準時が9時から17時になる拠点の所属員数の累計値を、開始時間をキー、対応する累計値を値とするハッシュにまとめ、値の最大値を出力すればよい。

上が本番の提出コード。Cでやったのが以下。

typedef struct {
    int hour;
    int num;
} table;
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 6
#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);
    }
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    int* w=(int*)malloc(sizeof(int)*n);
    if(w==NULL){
        printf("memory allocation to w failed\n");
        exit(1);
    }
    int* x=(int*)malloc(sizeof(int)*n);
    if(x==NULL){
        printf("memory allocation to x failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to w failed\n");
            exit(1);
        }
        fgets(s2,N2,stdin);
        *(x+i)=readint(s2);
        int j=0;
        while(*(s2+j)!=32)j++;
        j++;
        *(w+i)=readint(s2+j);
//        printf("w[%d]=%d x[%d]=%d\n",i,*(w+i),i,*(x+i));
        free(s2);
    }
    table* t=(table*)malloc(sizeof(table)*24);
    if(t==NULL){
        printf("memory allocation to t failed\n");
        exit(1);
    }
    for(int i=0;i<24;i++){
        (t+i)->hour=i;
    }
    for(int i=0;i<n;i++){
        int start=9;
        int end=17;
        start=((start+*(w+i))%24);
        end=((end+*(w+i))%24);
//        printf("start=%d end=%d\n",start,end);
        if(start<end){
            for(int j=start;j<=end;j++){
                ((t+j)->num) += *(x+i);
            }
        }else{
            for(int j=start;j<=23;j++){
                ((t+j)->num) += *(x+i);
            }
            for(int j=0;j<=end;j++){
                ((t+j)->num) += *(x+i);
            }
        }
    }
    int max=0;
    for(int i=0;i<24;i++){
        //printf("from %d: %d person\n",i,(t+i)->num);
        if ((t+i)->num > max){
            max=(t+i)->num;
        }
    }
    printf("%d\n",max);
    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;
}
    

ほぼ出来上がったころに気づいたのだが、構造体tableのhourはインデックスと同じなので、構造体を作らなくても単なる配列でもよかった。


複数の最大値

ABC329-D

問題概要

1からnまでの整数のいずれかを要素とする配列(要素数m)が与えられる。i番目までの中で最も多く出現している数(ただし、最も多く出現している数が複数あるときは、そのうち最も小さい数)を出力せよ。

解答

n,mは最大で2*10^5なので、ほぼ二重ループになる下記のコードでTLEになることは分かっていたが、この日は開始から約30分後に参加登録したにもかかわらず、A,B問題はおろかC問題まであっさり解け、それで終了までかなり時間があったため、これもやってみることに。あくまでも、小規模なデータならこれで解けるということで書いたコードである。

n,m=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
c=Hash.new
(0...m).each do |i|
    if c.has_key?(a[i])
        c[a[i]]+=1
    else
        c[a[i]]=1
    end
    max=0
    idxs=[]
    c.each do |k,v|
        if v>max
            idxs.clear
            idxs.push(k)
            max=v
        elsif v==max
            idxs.push(k)
        end
    end
    puts idxs.min
end
    

i番目まで数えた時の各要素の出現回数を、要素の値をキー、出現回数を値とするハッシュで保持、最大回数出現する要素を配列に記録し、その配列の最小値を出力している。

2022年11月10日木曜日

出力形式に関する問題

小数の出力形式

ABC274-A

問題概要

A/Bを小数第4位で四捨五入した値を末尾ゼロを省略せずに表記して出力せよ。

解答

a,b=gets.chomp.split(" ").map(&:to_i)
printf("%.3f\n", (b.to_f/a).round(3))
    

aかbのどちらかを小数に変換して割り算し、そのオブジェクトからroundメソッド呼び出して引数に表示したい小数点以下の桁数を渡す。

あとはprintfが0埋めをやってくれる。

どうということのない問題なのだが、実際には改めて調べなおしてやることになった。

上が本番の提出コード。Cでやったのが以下。

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);
    }
    fgets(s1,N1,stdin);
    int a=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int b=readint(s1+j);
    free(s1);
//    printf("a=%d, b=%d\n",a,b);
    int quot10000=b*10000/a;
//    printf("quot10000=%d\n",quot10000);
    int arr[5];
    for(int i=0,divisor=10000;i<5,divisor>0;divisor/=10,i++){
        arr[i]=quot10000/divisor;
        quot10000-=arr[i]*divisor;
//        printf("arr[%d]=%d\n",i,arr[i]);
    }
    char str[6];
    str[0]=arr[0]+48;
    str[1]=46;
    str[2]=arr[1]+48;    
    str[3]=arr[2]+48;
    if (arr[4]<5){
        str[4]=arr[3]+48;
    }else{
        str[4]=arr[3]+1+48;
    }
    str[5]=0;
    printf("%s\n",str);
    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;
}
    

16進数に変換し、先頭に0埋め

ABC271-A

問題概要

10進法で表された整数nが与えられる。(1≦n≦255) nを二桁の16進数として出力せよ。ただし、二桁未満になる時は、先頭に0を埋める。

解答

n=gets.to_i
s=[]
q,x=n.divmod(16)
h={0=>"0",1=>"1",2=>"2",3=>"3",4=>"4",5=>"5",6=>"6",7=>"7",8=>"8",9=>"9",10=>"A",11=>"B",12=>"C",13=>"D",14=>"E",15=>"F"}
s.push(h[q])
s.push(h[x])
puts s.join("")
    

これが本番の提出コード。

0から15の数字をキー、"0"から"F"までの文字を値とするハッシュを作り、与えられた数字を16で割った商をキーとする値を16の位に、余りをキーとする値を1の位に入れている。

もっとも、rubyにはもともと10進数を16進数に変換する機能が備わっているので、以下のようにそちらを利用してもよい。

n=gets.to_i
nn=sprintf("%02X",n)
puts nn
    

本番でこれを使わなかったのは、これだと先頭に0xが入ると思い込んでいたからなのだが、やってみたら0x無しで出力できた。

Cでやったのが以下。

char convert_to_hexadeca_1(int n);
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);
    }
    fgets(s1,N1,stdin);
    int n=readint(s1);
//    printf("n=%d\n",n);
    int n1=n/16;
    int n2=n%16;
//    printf("s0=%d, s1=%d\n",n1,n2);
    char str[3];
    str[0]=convert_to_hexadeca_1(n1);
    str[1]=convert_to_hexadeca_1(n2);
    str[2]=0;
    printf("%s\n",str);
    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;
}
char convert_to_hexadeca_1(int n){
    if (n>=16){
        printf("n must be less than 16.\n");
        exit(1);
    }
    if (n<0){
        printf("n must not be negative.\n");
        exit(1);
    }
    char c;
    if (n>=0 && n<10){
        c=n+48;
    }else if (n>9 && n<16){
        c=n-10+65;
    }
    return c;
}
    

時刻の表示

ABC258-A

問題概要

21時からk分後の時刻をhh:mmの形で出力せよ。

解答

n=gets.to_i
m=s=0
m,s=n.divmod(60)
m+=21
printf("%02d:%02d\n",m,s)
    

上が本番の提出コード。Cでやったのが以下。

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);
    }
    fgets(s1,N1,stdin);
    int k=readint(s1);
//    printf("k=%d\n",k);
    int hour=21+k/60;
    int minute=k%60;
//    printf("hour=%d, minute=%d\n",hour,minute);
    char str[6];
    str[0]=hour/10+48;
    str[1]=hour%10+48;
    str[2]=58;
    str[3]=minute/10+48;
    str[4]=minute%10+48;
    str[5]=0;
    printf("%s\n",str);
    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埋め

ABC254-A

問題概要

3桁以上の整数が与えられる。その下2桁を、xxの形で出力せよ。

解答

n=gets.to_i
t1=(n%10).to_s
n/=10
ans=(n%10).to_s
ans+=t1
puts ans    
    

上が本番の提出コード。

1の位の数と10の位の数をいったん文字列にしてから連結している。

後になって考えると、次のようにした方が簡単だったのだが。

n=gets.to_i
printf("%02d\n",n%100)
    

Cでやったのが以下。

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);
    }
    fgets(s1,N1,stdin);
    int n=readint(s1);
//    printf("n=%d\n",n);
    int n10=(n/10)%10;
    int n1=n%10;
//    printf("n10=%d, n1=%d\n",n10,n1);
    char str[3];
    str[0]=n10+48;
    str[1]=n1+48;
    str[2]=0;
    printf("%s\n",str);
    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;
}    
    

切り捨て

ABC314-A

問題概要

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679の小数第n位以下を切り捨てて出力せよ。

解答

最初、問題を、「小数第n+1位で四捨五入せよ」というものだと勘違いしてしまって書いたのが次のコード

n=gets.to_i
s="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
ans=""
t=s[n+2].to_i
if t>=5
    ss=s[n+1].to_i
    ss+=1
    if ss==10
        s[n+1]="0"
        sss=s[n].to_i+1
        if sss==10
            s[n]="0"
            s[n-1]=(s[n-1].to_i+1).to_s
        else
            s[n].sss.to_s
        end
    else
        s[n+1]=ss.to_s
    end
end
(2+n).times do |i|
    ans[i]=s[i]
end
puts ans
    

9が最大で何個まで続いているか分からなければまた別の処理が必要になるが、この問題の文字列では9は2つまでしか連続していないから、これで十分。なぜかWAがいくつか出るので、いったん放棄していたのだが、B問題をやったあと改めて見直してみて問題の勘違いに気付いた。それで書き直したのが以下。

n=gets.to_i
s="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
ans=""
t=s[n+2].to_i
(2+n).times do |i|
    ans[i]=s[i]
end
puts ans
    

これでACになった。有り得ないような勘違いだったのだが、まさか単なる切り捨てのような簡単すぎる問題は出ないだろうという思い込みで問題文を読み違えてしまったようだ。

Cでやったのが以下。

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);
    }
    fgets(s1,N1,stdin);
    int n=readint(s1);
//    printf("n=%d\n",n);
    char pi[]="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
    char str[103];
    str[0]=pi[0];
    str[1]=pi[1];
    for(int i=1;i<=n;i++){
        str[i+1]=pi[i+1];
    }
    str[n+2]=0;
    printf("%s\n",str);
    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;
}