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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

503閲覧

配列の動的割付け 宣言

program777

総合スコア7

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/06/12 04:35

「トランプ問題」
ジョーカーを除いた52枚のトランプカードが上から順番に
s:スペード, C:クローバー,D:ダイヤ, H:ハートとして
SK SQ SJ S10 S9 S8 S7 S6 S5 S4 S3 S2 SA CK CQ CJ C10 C9 C8 C7 C6 C5 C4 C3 C2 CA DK DQ DJ D10 D9 D8 D7 D6 D5 D4 D3 D2 DA HK HQ HJ H10 H9 H8 H7 H6 H5 H4 H3 H2 HA
積まれています。
この山をreverse関数を用いて、昇順します。以下のようになります。
HA H2 H3 H4 H5 H6 H7 H8 H9 H10 HJ HQ HK DA D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK CA C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK SA S2 S3 S4 S5 S6 S7 S8 S9 S10 SJ SQ SK
これを上から任意の n 人の席にカードを配布してから重ねて一つにまとめるシャッフル を、以下の関数 dealnで実装します。
ここで、dealn関数ないで、配列の動的確保を行う際の宣言として、
*Card h;
h = new Card[n];

上のように宣言しましたが、うまく動きません、、、.
今回の場合の配列の動的確保の仕方について教えていただきたいです。
また、このプログラムをn==5のときで実行すると、以下のようになります。
SQ S7 S2 C10 C5 DK D8 D3 HJ H6 HA SK S8 S3 CJ C6 CA D9 D4 HQ H7 H2 S9 S4 CQ C7 C2 D10 D5 HK H8 H3 S10 S5 CK C8 C3 DJ D6 DA H9 H4 SJ S6 SA C9 C4 DQ D7 D2 H10 H5

#include <iostream>
using namespace std;

class Card {//トランプの一枚のカード
private:
int suit; //0-3 各々がH(Heart), D(Diamond), C(Clover), S(Spade)に対応
int number; //1-13
Card *next; //一枚下にあるカードを指すポインタ

public:
Card(int i, int j, Card current_next=NULL){suit = i; number = j; next = current_next;}
Card(int n)
friend ostream& operator<<(ostream&, Card);
friend void printCards(Card
head);
friend Card *split(Card *, int );
friend void pileup(Card *, Card *);
};

// Cardの出力オペレータ
ostream& operator<<(ostream &fout, Card c){
// suit = 0, 1, 2, 3 をH, D, C, Sに変換してsuitを表示
if(c.suit==0) fout << "H";
else if(c.suit==1) fout << "D";
else if(c.suit==2) fout << "C";
else fout << "S";
// number = 1 の時はA(Ace)を表示
// number = 2-10の時はそのまま数を表示
// number = 11, 12, 13 の時はJ(Jack), Q(Queen), K(King)を表示
if(c.number==1) fout << "A";
else if(c.number==11) fout << "J";
else if(c.number==12) fout << "Q";
else if(c.number==13) fout << "K";
else fout << c.number;
// 末尾にスペースを一つ表示
fout << ' ';

return fout;
}

//一番上のheadから順番に全てのカードをたどって表示する関数
void printCards(Card *head){

Card *current;

current = head;
int i = 0;
while(current != NULL){//次のカードがなくなるまで繰り返す
//currentの内容を表示し、次のカードに移動
cout << *current;
current = current->next;
// 13枚毎に改行する
i++;
if(i == 13){
cout << endl;
i = 0;
}
}
}

// 一番上(head)から n 枚カードを残しその後に続くカードを切り取って 2 組に分割する関数
// 実行後 head 自体は変化なく残った n 枚の組の一番上となる
// 切り取られた組の一番上のカードのポインタが return される
Card *split(Card *head, int n){
Card *p;
Card *current;
current = head;
int i = 1;
while(i!=n){
if(current->next!=NULL){
current=current->next;
i++;
}
else
i++;
}
p = current->next;
current->next = NULL;
return p;
}

// head1 のカードの組を head2 の組の上にのせて 1 組にする関数
// 実行後 head1 は変化がなくまとめた組の一番上となる
void pileup(Card *head1, Card *head2){
Card *last;
last = head1;
while(last->next!=NULL){
last = last->next;
}
last->next = head2;
}

// 与えられたリストを逆順にしたリストを返す関数
Card *reverse(Card *head){
Card *new_head, *reverse_head;
reverse_head = NULL;
// カードを 1 枚ずつ head の上から取り去る
// そのカードを別の場所に 1 枚ずつ積んでいく
// 結果として逆順に積まれる
while(head != NULL){
new_head = split(head,1);
pileup(head,reverse_head);
reverse_head = head;
head = new_head;
}
return reverse_head;
}
Card dealn(Card head,int n){
** Card h
,p;
** h = new Card[n];

for (int i = 0; i < n; i++){
h[i] = NULL;
}
int count = 0;
while(head!=NULL){
p = split(head, 1);
pileup(head, h[count]);
h[count] = head;
head = p;
count++;
if(count==n)
count = 0;
}
for (int i = 1; i < n;i++){
pileup(h[0], h[i]);
}
return h[0];
}
int main() {

Card *head;//一番上のカードを指すポインタ

for(int i = 0; i < 4; i++){
for(int j = 1; j <= 13; j++){
// 最初のi=0,j=1の時のみ、next=NULLとしてカードを生成
// それ以降は現在のheadをnextとしてカードを生成し、新しいカードをheadとする
if(i==0 && j==1)
head = new Card(0, 1, NULL);
else
head = new Card(i, j, head);
}
}

// カードの表示
int n=5;
head=dealn(head,n)
printCards((head));

return 0;
}

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

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

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

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

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

guest

回答3

0

ベストアンサー

(1)そもそもコンパイル通らないので文法エラーを直すこと.

(2)reserveをコールする

reverse関数を用いて、昇順します

まず,この話が存在していない.,dealn()の呼び出しよりも前にhead = reverse(head);とか書く必要がある.

(3)dealn()の中身
とりあえずこんな感じで所望の動作はするのでは,と思われる.

Card *dealn(Card *head,int n){ Card **h,*p; //hの型 h = new Card*[n]; //newで作る物の型 for (int i = 0; i < n; i++){ h[i] = NULL; } int count = 0; while(head!=NULL){ p = split(head, 1); pileup(head, h[count]); h[count] = head; head = p; count++; if(count==n) count = 0; } for (int i = 1; i < n;i++){ pileup(h[0], h[i]); } //new[] した物は delete[] する Card *HeadOfResult = h[0]; delete[] h; return HeadOfResult; }

質問内容とは直接関係ないけど,せっかくだからnewが不要な簡単な方法を追記.

C++

1//パケットを2つに分けるが,splitとは戻り値と引数が異なる(逆な感じ)関数. 2//渡されたパケットを,上側n枚 と 残り の2つのパケットに分けて 3//前者側のトップを指すポインタを戻り値とする. 4//headは後者側のトップを指す値に更新される. 5Card *split2( Card *&head, int n ) 6{ 7 Card *p = split( head, n ); 8 std::swap( p, head ); 9 return p; 10} 11 12//new不要なやりかた 13Card *dealn2(Card *head,int n) 14{ 15 //結果のカードは机上に積んでいくものとする 16 Card *DesktopPile = nullptr; 17 //まず右手にデックを持つ 18 Card *RHS_Packet = head; 19 //左手は最初は空である 20 Card *LHS_Packet = nullptr; 21 //--- 22 int nSkip = n; 23 while( true ) 24 { 25 --nSkip; 26 while( RHS_Packet ) 27 { 28 //右手のパケットのトップからnSkip枚のカードを取り上げ,それを 29 //左手のパケットのボトム側に付け加える 30 if( nSkip>1 ) 31 { 32 Card *SkippedCards = split2( RHS_Packet, nSkip ); 33 if( SkippedCards ) 34 { 35 if( LHS_Packet ){ pileup( LHS_Packet, SkippedCards ); } 36 else{ LHS_Packet = SkippedCards; } 37 } 38 } 39 //右手にカードが残っていないならループを抜ける 40 if( !RHS_Packet )break; 41 //右手のパケットのトップのカードを机上の山のトップに移す(重ねる) 42 Card *CardToDeal = split2( RHS_Packet, 1 ); 43 pileup( CardToDeal, DesktopPile ); 44 DesktopPile = CardToDeal; 45 } 46 47 //左手にカードが無いなら終了 48 if( !LHS_Packet )break; 49 //左手にあるパケットをそのまま右手に移す 50 RHS_Packet = LHS_Packet; 51 LHS_Packet = nullptr; 52 } 53 return DesktopPile; 54}

投稿2020/06/12 06:38

編集2020/06/12 09:32
fana

総合スコア11658

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

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

fana

2020/06/12 06:41

カードの束を単方向リストで表すという方針が,そもそもめんどくせぇ…
episteme

2020/06/12 07:03 編集

激しく同意。vector<Card>でええやんなぁ。 リストがお望みならわざわざ自前で作らんでも list<Card> か forward_list<Card> で。
fana

2020/06/12 09:38 編集

(というか,質問コードに沿った修正案を書いているのに華麗にスルーされている理由がわからん.「n==5のときで実行すると、以下のようになります」を満たしているハズなのだが.)
program777

2020/06/12 10:03

なるほどですね。。。 解決しました! とても分かりやすかったです。 ご丁寧にありがとうございました。
fana

2020/06/12 10:45

動くコードの入手が目的ではなく,何がいけなかったのか等の把握が目的であるならば,そこらへんのことがちゃんと「なるほど」していると良いのですが……
guest

0

一組のデッキをn人に分配するんですよね。
そーすっと n人分の Cardの数珠つなぎを作り、n人分の Card*(手札の先頭)を返すんですよね。
つまり Card* をn個返したいんですよね。

ならば:

C++

1Card** dealn(Card *head,int n){ 2 Card** h = new Card*[n]; // Card*をn個確保する 3 ... 4 return h; 5}

じゃなくて?

※ あるいは vector<Card*> 返してもいい。

投稿2020/06/12 05:44

episteme

総合スコア16614

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

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

program777

2020/06/12 06:00

Cardをn個確保し、一組のデッキをそれぞれ順番に分配します。 今回だとn=5であるので。 分配した5つの山をpileup関数で結合し、 最終的に結合した山の先頭ポインタをreturn します。
episteme

2020/06/12 06:20 編集

n人に一枚ずつ配るイメージ? だったら Card* h = new Card[n]; でいいけど。 だとすると h[count] = head; はオカシイ。 左辺は Card で 右辺は Card* だから。 (Card*をn個返した方が楽だと思うけどなぁ...) # 「うまく動きません」はヤメテ。その一言に何のてがかりもない。
program777

2020/06/12 06:33

そうですね、通常のトランプの配り方のようにn人に1枚ずつ配っていくイメージです。
program777

2020/06/12 06:51

h[count] = head;で左辺と右辺で型をあわせるために、 最初に頂いた下のコードを使うと、 Card** dealn(Card *head,int n){ Card** h = new Card*[n]; // Card*をn個確保する ... return h; } main関数で、printcardの引数としてhを用いるときに、 printCardsの引数の型がCard *,に対してhはCard**であることから エラーが出てしまいます。 型を合わせるための方法などありますか?
episteme

2020/06/12 07:01

printCards(h[2]); // プレイヤー2番の手札をプリント ...なにか問題ある?
program777

2020/06/12 08:26

訂正し、型もあっていてコンパイルも通るのですが、 printCards(h[2])で実行すると このようにでます。。。 H5 H10 D2 D7 DQ C4 C9 SA S6 SJ H4 H9 DA D6 DJ C3 C8 CK S5 S10 H3 H8 HK D5 D10 C2 C7 CQ S4 S9 結合した山の先頭ポインタはh[0]なのでprintCards(h[0])ではないでしょうか? ただh[0]で実行してもこのようになります。 H2 H7 HQ D4 D9 CA C6 CJ S3 S8 SK HA H6 HJ D3 D8 DK C5 C10 S2 S7 SQ H5 H10 D2 D7 DQ C4 C9 SA S6 SJ H4 H9 DA D6 DJ C3 C8 CK S5 S10 H3 H8 HK D5 D10 C2 C7 CQ S4 S9 ・ソースコード Card **dealn(Card *head,int n){ Card *p; Card **h = new Card*[n]; for (int i = 0; i < n; i++){ h[i] = NULL; } int count = 0; while(head!=NULL){ p = split(head, 1); pileup(head, h[count]); h[count] = head; head = p; count++; if(count==n) count = 0; } for (int i = 1; i < n;i++){ pileup(h[0], h[i]); } return h; } int main() { Card *head;//一番上のカードを指すポインタ for(int i = 0; i < 4; i++){ for(int j = 1; j <= 13; j++){ // 最初のi=0,j=1の時のみ、next=NULLとしてカードを生成 // それ以降は現在のheadをnextとしてカードを生成し、新しいカードをheadとする if(i==0 && j==1) head = new Card(0, 1, NULL); else head = new Card(i, j, head); } } // カードの表示 int n = 5; Card **h = dealn(head, n); printCards(h[2]); return 0; }
episteme

2020/06/12 08:38 編集

えーと...dealnは何をしたいんです? n = 3 とすると 手札1枚の組を3つと、残りの山札に分割するんですか?
guest

0

配列を使うのはやめて、std::vectorを使いましょう

投稿2020/06/12 04:53

yuki23

総合スコア1448

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

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

program777

2020/06/12 05:21

std::Vectorを使うと、 std::vector<Card> *h(n); こんな感じですか? エラー出てるので違うと思いますが
fana

2020/06/12 05:45

低評価の理由: newを使ったコードで「うまく動きません、、、」ならば,newを自分で書くのをやめてvectorを使ったとしても何かが解決するとは思えない. (同じ型のものを,同じ個数だけ作って,同じように使うならば,結果は同じでは?  もちろん,解放に関する部分で動作に差が発生するだろうけども,それが本件を解決する要素なのか?)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問