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

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

新規登録して質問してみよう
ただいま回答率
85.37%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1575閲覧

競技プログラミングの問題を教えていただきたいです(正整数の立方数による表現)

raiboz1115

総合スコア4

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

3クリップ

投稿2021/06/17 07:32

編集2021/06/17 09:21

C言語

1#include<stdio.h> 2int amari(int , int); 3int kosuu(int, int); 4int keisan(int, int); 5int sum = 0, min = 11; 6 7int main(void){ 8 int n, m, kans = 0; 9 scanf("%d%d", &n, &m); 10 int i, k, ans = 100; 11 if(n == 1){ 12 k = 1; 13 }else{ 14 for(i = 1 ; i < n ; i++){ 15 if(i*i*i > n){ 16 k = i-1; 17 break; 18 } 19 } 20 } 21 for(;k > 1;k--){ 22 kans = keisan(n, k); 23if(kans < ans){ 24 ans = kans; 25 } 26 sum = 0; 27 } 28 if(ans <= m){ 29 printf("%d\n", ans); 30 }else{ 31 printf("-1\n"); 32 } 33} 34 35int amari(int n, int p){ 36 return n % (p*p*p); 37} 38 39int kosuu(int n, int p){ 40 return n / (p*p*p); 41} 42int keisan(int n, int k){ 43 int q = 0; 44 if(k != 1){ 45 sum += kosuu(n, k); 46 keisan(amari(n, k), k-1); 47 }else{ 48 sum += n; 49 } 50 return sum; 51} 52 53 54```競技プログラムの問題を教えていただきたいです。(C言語でお願いいたします) 55【問題原文】 56正整数 n と m が与えらえるとき,n を m 個以下の立方数(正整数の三乗)の和で表現することを考える. 57例えば,n = 35,m = 10 のとき,n は次のように立方数を使って表現できる 5835 = 33 + 23 5935 = 33 + 13 + 13 + 13 + 13 + 13 + 13 + 13 + 13 6035 = 23 + 23 + 23 + 23 + 13 + 13 + 13 61正整数 n が m 個以下の立方数の和として表現できるとき,必要な立方数の最小個数を調べるプログラムを作成せよ. 62(入力) 63入力は以下の形式で与えられる. 64n m 65(制約) 66・n,m はいずれも正整数. 671 ≤ n ≤ 50000 681 ≤ m ≤ 10 69(出力) 70正整数 n を表現するのに必要な立方数の最小個数を出力せよ. ただし,n が m 個以下の立方数の和で表現できないときには -1 と出力せよ. 71(例) 72入力1: 7335 10 74出力1: 752 7635 を表現するのに必要な立方数の最小個数は2個である(35 = 33 + 23). 77入力2: 7853 3 79出力2: 80-1 81533個以下の立方数の和として表現することはできない. 82 83自分で作成したソースコードを載せさせていただきました。 84上記のコードでは例の入力1, 2には対応できているのですが、自分でほかの値で試したときに正しい値を出力しませんでした。(具体的にはn=4059, m=10この場合の正しい答えは12^3+11^3+10^33、自分のコードでは5と出てしまいます。) 85理由はわかっているのですが、改善方法がわかりません。

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/06/17 07:33

どこまで実装してみましたか?
y_waiwai

2021/06/17 07:41

まずはあなたのコードを提示しましょう 競技プログラムならなおさら
raiboz1115

2021/06/17 07:42

返信ありがとうございます。 ソースコードを張らせていただきます。 #include<stdio.h> int amari(int , int); int kosuu(int, int); int keisan(int, int); int sum = 0, min = 11; int main(void){ int n, m; scanf("%d%d", &n, &m); int i, k, ans; for(i = 1 ; i < n ; i++){ if(i*i*i > n){ k = i-1; break; } } ans = keisan(n, k); if(ans <= m){ printf("%d\n", ans); }else{ printf("-1\n"); } } int amari(int n, int p){ return n % (p*p*p); } int kosuu(int n, int p){ return n / (p*p*p); } int keisan(int n, int k){ if(k != 1){ sum += kosuu(n, k); keisan(amari(n, k), k-1); }else{ sum += n; } return sum; } 上記のコードでは例の入力1, 2には対応できているのですが、自分でほかの値で試したときに正しい値を出力しませんでした。
y_waiwai

2021/06/17 07:45

質問文は編集できますんで、そこにコードを貼ってください コードは、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
raiboz1115

2021/06/17 07:45

一応提出もしてみたのですが、WRONG-ANSWERではなくRUN-ERROER判定となってしまいました。
raiboz1115

2021/06/17 07:46

指摘ありがとうございます。 質問文を編集させていただきます。
raiboz1115

2021/06/17 15:53

指摘ありがとうございます。 yahoo知恵袋の方は削除させていただきました。
kazuma-s

2021/07/21 21:53 編集

> 35 = 33 + 23 35 = 3^3 + 2^3 または 35 = 3**3 + 2**3 と書いてほしい。 最初見た時、なんだこれ?、と思いました。
guest

回答3

0

ベストアンサー

この問題は「最小個数部分和問題」と呼ばれる典型的な動的計画法で解く問題です。
参考:
https://qiita.com/drken/items/a5e6fe22863b7992efdb
ナップザック問題の解法が分かっていればその応用で解けます。

一般的には表にすると分かりやすいです。
例題通り、n=35, m=10を考えます。

横列を0からターゲットである35までの数列(ni)、縦行を0からmまでの数列(mi)とする表を作成します。
この表の中には、「niをmiまでの数で作成した平方数(T[1..mi])の和で作成した際の、平方数の最小個数」を入れるものとします。ただし、初期値として(0,mi) = 0 、それ以外のマスをm+1とします。m+1は「不可能」を表すと考えてください。(一般的には+∞を入れますが、この場合はm+1で良い)

イメージ説明

mi = 1 から順番にそれぞれの ni に対して「最小いくつの立方数で作成できるか」を考えていきます。
mi = 1 の場合は、1 の平方数は 1 なので それぞれの ni に対して 1を何回足し算できるかで求まります。

イメージ説明

赤字が差分です。
立法数が1のみだと、10までは作成できますがそれ以降は作成できません。当然ですね。

次に、mi=2 の行に注目します。
2の立方数は8(i の立法数を Ti と表現します)です。例えば ni=8 の場合、立法数8だけで構成できるので(8,2)に入る数値は1になるでしょう。
ni=9 の場合はどうでしょう。もともと 9 は 1 の平方数 9個で構成できます。しかし、1 と 8 の2つでも構成できます。こちらの方が構成数が小さいですね。
これは、ni=1 の現段階での最小構成に 8 を加算した場合に等しいです。
したがって、「miが1つまで段階での最小数(1行上の数字)」と「miが現段階で、ni - Ti の最小数 + 1」のどちらか小さい方が現時点での回答になります。

イメージ説明

mi = 2 の行を ni = 1 から上記の方法で順番に埋めていくとこのような表になっていきます。

イメージ説明

同様に mi = 3 の行も同じルールで埋めていきます。

イメージ説明

mi = 4 の立法数 64 は既にn=35を超えているので、これ以上探索する必要はありません。ここで打ち切りとします。
最終的に、ni=35 の列の一番下の数値である 2 が回答となります。

以上のような計算をプログラムで行えばよいです。コーディングのサンプルは冒頭のqiita記事を参考にしてください(C++のコードですが入出力以外はCとほぼ同等なので十分参考になると思います)
質問者さんは貪欲法での実装ができているので動的計画法の実装も理屈が分かれば難しくないでしょう。

それでは全て正解できるよう頑張ってください。

投稿2021/06/21 05:58

hope_mucci

総合スコア4447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raiboz1115

2021/07/21 17:45

ありがとうございます!時間はかかってしまいましたが何とか理解することができました!
guest

0

面白そうな問題なので、やってみました。

C

1#include <stdio.h> // scanf, printf 2#include <math.h> // cbrt 3 4int n, m, len = -1; 5 6void gen(int i, int j, int k) 7{ 8 if (k == 0) len = m = i; 9 else if (k > 0 && i < m) 10 for (; j > 0; j--) gen(i+1, j, k - j*j*j); 11} 12 13int main(void) 14{ 15 scanf("%d%d", &n, &m); 16 gen(0, (int)(cbrt(n) + 0.1), n); 17 printf("%d\n", len); 18}

総当たりですが、枝刈りしているので短時間で済むでしょう。
バグのご指摘は歓迎します。

投稿2021/07/21 21:35

編集2021/07/23 05:45
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ナイーブに、「最大の立方数から埋めていく」というパターンでは、正解が出ない数があります。

たとえば、855 = 8**3 + 7**3と立方数2つで表されますが、このアルゴリズムでは先に9**3を引いてしまうので、855 = 9**3 + 5**3 + 1**3の3つと出てしまいます。

投稿2021/06/17 07:47

maisumakun

総合スコア145930

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raiboz1115

2021/06/17 07:53

なるほど、理解しました。 問題意向を満たすにはどのようなアルゴリズムがありますでしょうか? 加えて、答えが間違って出力されるのであればWRONG-ANSWERと判定されるはずなのですがRUN-ERROER判定となる原因として何が考えられるでしょうか?
maisumakun

2021/06/17 07:56

n=1の場合にkが初期化されない、という問題がまずありそうです。
raiboz1115

2021/06/17 08:47

ありがとうございます。 RUN-ERRORではなくWRONG-ANSWERにはなりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問