1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【ABC308】FortranでA,B,C問題

Last updated at Posted at 2023-07-07

A - Weekly Records

問題文

与えられた8個の整数について、以下の3つの条件を全て満たすかどうかにより、 Yes/Noを出力します。

  1. 数列 (S1,S 2,…,S8)はS1≤S2≤⋯≤S8ある。
  2. S1,S2,…,S8は全て100以上675以下である。
  3. S1,S2,…,S 8は全て25の倍数である。

詳細はこちらです。

解説

【フロー図】

【補足】
※1
「25の倍数であるかどうか」については、25で割った余りが0かどうかにより判定します。
プログラムに実装する際には、mod関数を使用します。

※2
S1〜S8のの整数が条件1〜3を全て満たす場合、Do文を抜けられるためYesを出力します。

※3
S1〜S8のの整数が条件1〜3を1つでも満たさない事が判明した場合、その時点でNoを出力し、プログラムを終了します。

プログラム例

program ABC308a
    implicit none
    integer(8) i, j, tmp
    integer(8) S(8)

    !初期化
    i = 0; j = 1; tmp = 0

    !入力
    read (*, *) S(:)

    !処理
    do i = 1, 8
        !S(i)<S(i+1)
        if (i < 8) then
            if (S(i) > S(i + 1)) then
                write (*, "(a)") 'No'
                stop
            end if
        end if
        if (S(i) > S(i + 1)) then
            write (*, "(a)") 'No'
            stop
        end if
        !S(i)が100以上 ,675以下
        if (S(i) < 100 .or. S(i) > 675) then
            write (*, "(a)") 'No'
            stop
        end if
        !S(i)が25の倍数
        if (mod(S(i), 25) /= 0) then
            write (*, "(a)") 'No'
            stop
        end if
    end do

    !結果の出力
    write (*, "(a)") 'Yes'
end

B - Default Price

問題文

N皿食べた料理の合計金額を算出します。
料理の価格は皿の色と対応しますが、定義されていない色の皿が出てくる事に注意が必要です。

詳細はこちらです。

解説

【フロー図】

【補足】
※1
Pは定義されていない色の皿の値段を格納するため、Mよりも1つ配列が大きくなります。
PとMについて、値段と色で配列の添え字がずれていると分かりにくいので、Mは1つ大きく配列を確保します。
P(1)に定義されていない色の皿の値段を格納します。
M(1)には何も格納しません。

※2
皿の色を調べ、一致すればtmpに単価を代入します。
どの色にも当てはまらない場合には、P(1)であるため、予めtmp=P(1)にしておきます。
一致する皿の色が見つかれば、tmpは上書きされるので問題ありません。

※3
一致する皿の色が見つかれば、tmpは上書きします。
また、これ以上は皿の色を調べる必要がないので、exit文によりdo文を抜けます。

プログラム例

program ABC308b
    implicit none
    integer(8) i, j, total, tmp
    integer(8) N, M
    character(20), allocatable :: C(:), D(:)
    integer, allocatable :: P(:)
    !初期化
    i = 0; j = 0; total = 0; tmp = 0

    !入力
    read (*, *) N, M
    M = M + 1 !対象外の皿の分があるので+1
    allocate (C(N), D(M), P(M))

    read (*, *) C(:)
    read (*, *) (D(i), i=2, M)
    read (*, *) P(:)

    !処理
    do i = 1, N
        tmp = P(1)
        do j = 2, M
            if (C(i) == D(j)) then
                tmp = P(j)
                exit
            end if
        end do
        total = total + tmp
    end do

    !結果の出力
    write (*, "(i0)") total
end

C - Standings

問題文

コイントスをしたN人のうち、表の確率が高い人から順に出力します。
確率が同じ場合には、番号が若い順に出力します。

詳細はこちらです。

解説

【フロー図】

※1
Fortranでは配列の計算を一括で行う事ができます。
Do文でD(i)=A(i)/(A(i)+B(i))を適時計算せずとも、D=A/(A+B)1からNのDをまとめて計算する事ができます。

また、AとBの値の範囲が$0 <= A,B <=10^9$と非常に大きくなる可能性があるため、桁落ちの可能性があります。
今回は4倍精度で変数を定義し、桁落ちを回避しました。

※2
計算したDを大きい順(成功率の高い順)に並び替えます。
この時、確率が同じ場合は番号が小さい順に並び替える必要があります。
順番は小さい順から既に格納されていますので、確率が同じ場合は並び替えをしない事が求められています。
従って並び替えのアルゴリズムは、安定ソートである事が必須です。
そこで今回は、マージソートにより並び替えを行いました。

※3
並び替えの結果を出力します。
確率が同じ場合には、番号が小さい順にから出力します。

プログラム例

module global_variable
    integer(4) N
end module
program ABC308c
    use global_variable
    implicit none
    real(16), allocatable :: A(:), B(:), D(:), tmp(:)
    integer(16), allocatable :: C(:), tmp2(:)
    integer(16) start, end, i

    !初期化
    i = 0
    read (*, *) N
    allocate (A(N), B(N), C(N), D(N))
    allocate (tmp(N), tmp2(N))

    !処理
    do i = 1, N
        read (*, *) A(i), B(i)
        C(i) = i
    end do
    D = (A/(A + B))
    start = 1; end = N
    tmp = 0; tmp2 = 0
    call margesort(D, C, tmp, tmp2, start, end)

    !結果の出力
    write (*, '(*(i0,1x))') C
end program
recursive subroutine margesort(x, y, tmp, tmp2, left, right)
    integer(16) left, right, mid
    integer(16) N
    real(16) x(N), tmp(N)
    integer(16) y(N), tmp2(N)
    integer(16) i, j, k

    !これ以上2分かつできないならretrun
    if (left >= right) return

    !分割できるだけ分割する
    mid = (left + right)/2
    call margesort(x, y, tmp, tmp2, left, mid)
    call margesort(x, y, tmp, tmp2, mid + 1, right)

    !並び替えの下準備としてtmpに配列をコピー
    j = 0
    tmp(left:mid) = x(left:mid)
    tmp2(left:mid) = y(left:mid)
    do i = mid + 1, right
        tmp(i) = x(right - j)
        tmp2(i) = y(right - j)
        j = j + 1
    end do

    !大小比較して小さい順に入れていく
    i = left
    j = right
    !write (*, '(3x,*(f13.101x),a)', advance='no') x(left:right)
    !write (*, '(a)', advance='no') '>>'
    do k = left, right
        if (tmp(i) > tmp(j)) then
            x(k) = tmp(i)
            y(k) = tmp2(i)
            i = i + 1
        else if (tmp(i) == tmp(j) .and. tmp2(i) < tmp2(j)) then
            x(k) = tmp(i)
            y(k) = tmp2(i)
            i = i + 1
        else
            x(k) = tmp(j)
            y(k) = tmp2(j)
            j = j - 1
        end if
    end do
    !write (*, '(3x,*(f13.10,1x))') x(left:right)
end subroutine margesort

感想

  • C問題について、約分や$10^{19}$を行うなどの工夫をし、桁落ちを回避する策がある様ですが、Fortranなら4倍変数で変数を用意すれば解決できます。
  • フロー図を入れる事で、記事の見やすさが改善しました。(フロー図を書く練習がしたいので、今後も使用します。)
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?