質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.49%

  • Python 3.x

    6843questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python3 等差数列 項数が不明の場合

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 237

問題)

二つの風呂 A,B に、特別なルールで水を貯めていくゲーム

Aの風呂には、あらかじめ、s リットルの水が含まれている。

A,B それぞれの風呂の容量は, a,b である。

入力値

s, a, b

条件
すべてのテストケースにおいて、以下の条件をみたします。

・1 ≦ S ≦ 1,000,000
・1 ≦ a, b ≦ 1,000,000
・a ≧ S + 10  ・すなわち、 風呂A には必ず 1 回以上水を行われる。

以下のルールに従って、水を貯めていく。

あらかじめ、s リットル入っているAの風呂に、10リットル の水をためる

次に、Bに (s + 10) リットルの 水を貯める

そして、A、B 交互に 1,010 リットル ずつの水を交互に入れる

水を入れ続け、溢れた方が負けである。

出力値には、勝った方の、すなわち溢れなかった方の風呂の名前と、

相手が溢れた直前に入れた、勝者の水の量を求めたい。

入力例1
1 1500 2050
出力例1
B 2021

入力例2
10 50 1019
出力例2
A 20

コード

入力例1
1 1500 2050
出力例1
B 2021

上記の入力値の場合、A,Bの風呂に入れる水の量は以下のような等差数列になる
Aの第1項、2項を除き、1010ずる増える

A: 1    11   1021  2031  3041  4051  5061   

B: 1011  2021  3031  4041  5051  6061  


入力例2
10 50 1019
出力例2
A 20


A:  10  20  1030  2040  3050  4060 

B: 1020  2030  3040  4050  5060  



s,a,b = map(int, input().split())
print(s,a,b)

#等差数列の項数が不明だが、n= 10 と仮定
l_1 = s+10
l_2 = s+1010
m = 1010
n = 10

#A,B の等差数列
lst_a = list(range(l_1, l_1+m*n, m))
lst_a.insert(0,s)

lst_b = list(range(l_2, l_2+m*n, m))

質問

今回の問題だと、等差数列の項数が不明のため式を作成できない

更に、ひとつの風呂の限界値は求められるが、ふたつ交互に入れていく場合の式が作れない

アドバイス等頂けたら嬉しいです。

試したこと

コード

s,a,b = map(int, input().split())
print(s,a,b)

#等差数列の項数が不明だが、n= 10 と仮定
l_1 = s+10
l_2 = s+1010
m = 1010
n = 10

#A,B の等差数列
lst_a = list(range(l_1, l_1+m*n, m))
lst_a.insert(0,s)

lst_b = list(range(l_2, l_2+m*n, m))

#1つの風呂の溢れる前の、入れた水の量を出力
for x in range(n):
    if lst_a[x] > a:
        break
print(lst_a[x-1])

追記質問

配列ではなく、while構文を利用するようにとアドバイスをいただきました。

A、B それぞれにwhile構文を使い、それぞれの浴槽の水が溢れる直前に入れた水の量を表示できた。

しかし、ルール上にあるように交互に入れた際の、勝った相手のみ表示させたい。

二つの条件を合わせたwhile文が書けません。

アドバイス等頂きたいです。

追記条件

加えて、入力値の条件を追記しました。

入力値

s, a, b

条件
すべてのテストケースにおいて、以下の条件をみたします。

・1 ≦ S ≦ 1,000,000
・1 ≦ a, b ≦ 1,000,000
・a ≧ S + 10  ・すなわち、 風呂A には必ず 1 回以上水を行われる。

コード

s,a,b = map(int, input().split())
print(s,a,b)

#Aの一回目
water_a = s+10
#Bの一回目
water_b = s+1010


#Bの一回目が限界値を越えていたら、Aの勝利
if water_b > b:
    print("A" + " " + water_a)
#上記以降は交互に入れる
else:
    while water_a < a:
        water_a += 1010
        print(water_a)
        break
#Bの一回目はクリアしたため、順次 1010リットルずつ足していく
    while water_b < b:
        water_b += 1010
        print(water_b)
        break

#出力値
1021 
2021
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2018/05/07 14:41 編集

    ルールでは最初に「Bに (s + 10) リットルの 水を貯める 」と書いてあるのに、コードではBの初期値が s+1010 になってる。どっちが正しいのか?

    キャンセル

回答 4

+4

風呂にたまる水の量が等差数列となることに気が付いているのであれば、数学的アプローチで答えを出すという方法もあります。
考え方として、水があふれるギリギリの項数を以下によって定式化します。

風呂の容量 < 2回目の水量 + 1010 × (n-1)

つまり上記を満たす最小のnを風呂aと風呂bに対して計算して結果の大きいほうが勝者であり、敗者のnで計算される勝者の水量が直前の水量ということになります。
解析的に答えを出すのであれば、

風呂の容量 = 2回目の水量 + 1010 × (n-1)

と置き換えた上で、n=…の形式に置き換えたものをコードにすればいいことになります。(答えが少数部を含む場合は切り上げが必要であり、整数の場合は1を加算する必要があります)

答えを出す計算式を手計算で出すことが面倒であれば、scipy.optimizerの中に便利な関数があるのでそちらを使うという方法もあります。上記の定式化であれば、minimize_scalarがいいかと思います。
まず、答えを出したい式を関数として定義し直します。

func(n) = np.abs(2回目の水量 + 1010 × (n-1) - 風呂の容量)

np.absはminimize_scalarを適用するため、答えではない場合は常に正数となるように補正するためのものです。
あとは、minimize_scalar(func)とすると、最小値(今回の場合は0)となるnを出力するので、これをもとに勝者判定とその時の水量を計算すればいいことになります。

少しマニアックな方法なので参考程度として見てください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/07 12:21

    コメントありがとうございます。

    斬新な数学的アプローチを知れてとても参考になりました。
    初心者のため、初見ですべて理解するのは難しいですが、時間をかけて吸収したいと思います。
    ご指導ありがとうございました。

    キャンセル

+2

出力値には、勝った方の、すなわち溢れなかった方の風呂の名前と、
相手が溢れた直前に入れた、勝者の水の量を求めたい。

プレイヤーAの第2ターンだけなんか特殊ですが、基本的にプレイヤーA、B両方について「さっき入れた水の量」と「風呂の残り容量」だけを変数に保持した上でwhile文を回せば十分です。
つまり配列を使う必要はありません。

算数的な考察を行えばもっと高速な解き方が得られそうな気もします。


追記: 表題の件、appendを使えばあとから要素数を増やせます(上記の理由でオススメしません)。

>>> foo = list()
>>> foo.append(10)
>>> foo.append(10)
>>> foo.append(10)
>>> foo.append(10)
>>> foo.append(10)
>>> foo
[10, 10, 10, 10, 10]

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/06 23:57

    コメントありがとうございます。

    アドバイスを活かして、while を利用しましたが、新たに疑問点にぶつかりました。
    大変恐縮ですが、追記質問致しました。
    アドバイス頂けたら幸いです。

    キャンセル

  • 2018/05/07 00:36

    while True などとしてループを作り、同一ループ中で「A注水」→「A敗北判定」→「B注水」→「B敗北判定」とすることで、交互に水を入れる実装ができそうです。

    キャンセル

  • 2018/05/07 12:26

    ありがとうございます。
    同一ループの中に組み込んで再編集してみます。

    キャンセル

checkベストアンサー

+1

何回読んでもルールがよくわからなかったので(どこが双方の第一ターンなのかわからない)、とりあえず適当に書いたのを。

s, a_max, b_max = map(int, input().split())

# initialize
a_water = s
b_water = 0

flag = True
# 1st turn
a_water += 10
if a_water > a_max and flag:
    winner = "B"
    last_water = b_water
    flag = False

b_water += s + 10
if b_water > b_max and and flag:
    winner = "A"
    last_water = a_water
    flag = False

if flag:
    # 2nd or later
    while True:
        a_water += 1010
        if a_water > a_max:
            winner = "B"
            last_water = b_water
            break

        b_water += 1010
        if b_water > b_max:
            winner = "A"
            last_water = a_water
            break
print(winner, last_water)

 修正点について

最初のコードはこういう挙動になっておりました。

1 5 5 # 入力
B 11 # 出力

こっちの方が望ましいと思うので直しました。

1 5 5
B 0

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/07 12:26

    コメントありがとうございます。

    こちらのコードで出力値を求めることができました。ベストアンサーに選ばさせて頂きました。
    while文と変数の定義が上手くできず、答えまで至らなかったです。
    hayataka2049さんのコードを参考に勉強します。

    ご指導ありがとうございました。

    キャンセル

  • 2018/05/07 14:09 編集

    すみません、1回目でどっちも溢れたときの判定が適当すぎました。修正版を上げたので、そっちも見てください

    キャンセル

  • 2018/05/07 15:10

    「1回目でどっちも溢れたとき」はありえません。
    というか、どちらかが溢れた時点で終了なので「どっちも溢れたとき」がありえません。

    キャンセル

  • 2018/05/07 15:16

    元のコードで先に両方加算してから判定するという手抜きをしていたので、それの修正をしたという意味です。

    キャンセル

  • 2018/05/07 15:36 編集

    「1回目でどっちも溢れたとき」はありえないので修正する意味は無いです。
    2行目に関しては取り消します。
    ちなみに 1 5 5 は入力条件に合っていないので検証する必要がありません。

    キャンセル

  • 2018/05/07 15:40

    私のルールの理解が足りなかったのですが、今理解しました。
    a ≧ S + 10  ・すなわち、 風呂A には必ず 1 回以上水を行われる。
    があるから、aは一回目の+10では溢れないということですね。了解です。ご指摘ありがとうございます。

    キャンセル

+1

bの初期値がs+1010の場合の回答です。

nowa = s + 10
#ここでAが溢れることはないのでチェック不要
nowb = s
while nowa <= a:
    nowb += 1010
    if nowb > b:
        break
    nowa += 1010

if nowa <= a:
    #Aの勝ち
    print("A", nowa)
else:
    #Bの勝ち
    print("B", nowb)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    6843questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。