壁打ちAtCoder

AtCoderの問題をひたすら解いてくブログです。思考やコードの書き方の私的備忘録として

AtCoder Beginner Contest 053 by C

AtCoder Beginner Contest 053について
C言語での回答

atcoder.jp


最適な方針を思いつくのに時間がかかったり、
条件分岐に気付くのに時間がかかったりしてしまい、非常に悔しい思いをしました。

解説:https://img.atcoder.jp/arc068/editorial.pdf

できたもの
A
B
C
できなかったもの
D

問題A

#include <stdio.h>

int main(void){
    int x;
    scanf("%d", &x);

    if(x<1200){
        printf("ABC");
    }
    else{
        printf("ARC");
    }

    return 0;
}

値が未満か以上で条件分岐

問題B

#include <stdio.h>
#include <string.h>

int main(void){
    int i,j;
    char s[200001];
    scanf("%s", s);
    int n = strlen(s);

    for(i=0;i<n;i++){
        if(s[i]=='A'){
            break;
        }
    }

    for(j=n-1;j>-1;j--){
        if(s[j]=='Z'){
            break;
        }
    }

    printf("%d", j-i+1);
    
    return 0;
}

文字列で一番最初に出てくるAの位置と、最後に出てくるZの位置を求めればOK
文字数なので差に1を足してやる

こっちを思いついたのが後だったので時間かかっちゃって悔しい。

最初に思いついたのは先頭から文字をチェックし、AがあったらそこからZが出てくるまでの最長文字数を求めるというもの。
でもこれだと"BACZZXZ"みたいなZが連続した時や後ろにある時に、一番後ろではなく先頭のZを拾ってしまい、ダメでした。

    int maxlen = 0;
    int countlen = 0;
    for(i=0;i<n;i++){
        if(s[i]=='A'){
            countlen++;
            for(j=i+1;j<n;j++){
                if(s[j]=='Z'){
                    countlen++;
                    break;
                }
                else{
                    countlen++;
                }
            }
        }
        if(maxlen<countlen){
            maxlen = countlen;
        }

問題C

#include <stdio.h>

int main(void){
    long long x;
    int i;
    scanf("%lld", &x);

    long long count;

    if(x%11==0){
        count = x/11 * 2;
    }
    else if(x%11>=1&&x%11<=6){
            count = x/11 * 2 + 1;
    }
    else{
        count = x/11 * 2 + 2;
    }

    printf("%lld", count);
    

    return 0;
}

解けたんですが、すごく時間がかかってしまい悔しいところ。
最短で最大値をとるためにはサイコロの目は656565…と変化させれば良いので、値を11で割り、余りがなければ割った数×2(11は6と5を一回ずつだしたことを意味するので操作の回数は2倍です)
余りがある場合は、余りが1~6の場合と7~10の場合で+1回か+2回の分岐です。

6565...とすればよいこと、11で割ってなんやかんやすればよいとはすぐに気づきましたが、5656...と6565...で分岐するなとかいらんことを考えてました。そこじゃない。11で割ったあまりによって分岐の部分に気付くのが遅く、えらい時間をくったのです。

問題D


以下解説より
操作を言い換えると、「2枚のカードを選んで取り除く」ということ。
k種類のカードがあるとすると、
・kが偶数の時、被ってないカードも1枚なくなるのでk-1
・kが奇数の時はk枚

種類ごとにカウントする(class[])まわりを参考にして合格しました。

#include <stdio.h>

int main(void){
    int n,i,j;
    int a;
    int class[100001] = {0};
    int max=0;
    scanf("%d", &n);

    for(i=0;i<n;i++){
        scanf("%d", &a);
        class[a]++;
    }

    for(i=0;i<100001;i++){
        if(class[i]>=1){
            max++;
        }
    }

    if(max%2==0){;
    	max = max-1;
    }

    printf("%d", max);

    return 0;
}

↓テストケース一つだけ時間超過になったコード。
テストケースの中身を見たところn=99999でO(n!)の計算になってたので当然な気も。

#include <stdio.h>

int main(void){
    int n,i,j;
    int a[100001];
    int class;
    int max;
    scanf("%d", &n);

    for(i=0;i<n;i++){
        scanf("%d", &a[i]);
    }

    //被っている数字があったら0にする
    for(i=0;i<n;i++){
        if(a[i]!=0){
            for(j=i+1;j<n;j++){
                //printf("%d番目は%d\n", i, a[i]);
                if(a[j]==a[i]){
                    a[j]=0;
                    //printf("%dと同じ%d番目は%d\n", a[i],j, a[j]);
                }
            }
        }
    }

    //0でない個数を数える
    for(i=0;i<n;i++){
      printf("%d\n", a[i]);
        if(a[i]!=0){
            class++;
        }
    }
    //printf("%d\n", class);

    if(class%2!=0){
        max = class;
    }
    else{
        max = class-1;
    }

    printf("%d", max);

    return 0;
}