大学の課題です。ヒントだけでもいいのでください 再帰関数です

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ぶーすと

大学の課題です。ヒントだけでもいいのでください 再帰関数です

#1

投稿記事 by ぶーすと » 6年前

問題4. キーボードから(空白類文字を含むかもしれない)1行を入力して,先頭のK文字 と最後の K 文字が順に同じであるかを判定するプログラムを書きたいとする.ただ し,K はマクロで定めなければならず,1 行の長さに上限を勝手に設けてはいけな く,メモリを動的に確保してはいけないとする.また,(入力のために打ち込んだ) 改行文字を判定に含めず,2K 文字未満の 1 行が入力された場合は「undecidable」 を表示すること.
入力例(K=5 のとき) 出力
abcdef_g_abcde same
1234567890123456 different
123456789012345 same
abcdef undecidable
繰り返し文や goto 文を使ってはいけない場合のプログラムを書け.

問題5. 負の整数が入力されるまで、繰り返しキーボードから整数を入力する。入力した 各整数 v について、v より後ろに入力された整数の中に v より大きいものがある ときのみ、v を画面に表示する関数 (配列・ポインタなどは用いない)。
たとえば、キーボードから
5 7 8 10 2 4 3 -1
を入力したときに画面に表示したいのは下記の通り:
2875

問題6. 文字’L’がl個、’R’がr個で構成される文字列をすべて表示する関数。ここで、 l と r は非負整数でキーボードから入力し、l + r ≤ 8 としてよい (高々8 文字が入 る配列を 1 つ用意すると良い)。(参考: ’L’ を選べれば選んで、残り l − 1 個と r 個の文字列作成。同様に’R’ を選べれば選んで · · ·。と両方行えばすべて行ったこ とになる。)

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#2

投稿記事 by かずま » 6年前

問題6 は、問題自体にヒントが書かれているので、その通り書くだけです。

コード:

#include <stdio.h>

char s[8]

void step(int l, int r, int n)
{
	l も r もゼロなら、  // これが再帰の終了条件
		s を出力。
	そうでないなら、
		l がゼロでなければ、
			s[n] に 'L' を入れて、step を呼び出す。
		r がゼロでなければ
			s[n] に 'R' を入れて、step を呼び出す。
}

int main(void)
{
	int l, r;
	scanf("%d%d", &l, &r);
	step(l, r, 0);
}
step の中で step を呼び出すときの引数を適当なものにしましょう。
なお、step の第3引数は、s の中で、次に 'L' または 'R' を入れる
場所であり、確定した 'L' と 'R' の個数でもあります。

フォーラムルールに従って、質問してもらえれば、
他の問題もヒントを差し上げましょう。

・使用するプログラミング言語は何ですか?
  C ですか、C++ ですか?
・OS とコンパイラは何ですか?
  gcc なら -Wall オプションを付けていますか?
  Visual C++ なら
   Visual Studio でデバッグしていますか?
   それとも、コマンドプロンプトで cl コマンドを使っていますか?

少しでもコードを書いて、エラーが出たら、
そのエラーをそのまま書いて質問してください。

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#3

投稿記事 by かずま » 6年前

これらの問題が難しいのは、様々な制約があるからですよね。
・繰り返し文(while や for)を使ってはいけない
・配列を使ってはいけない
・行の長さやデータの個数に上限があってはいけない
など。
これらの制約がなければ、易しいはずです。

そのプログラムを書いてもらえれば、
制約を満たすプログラムを書くヒントを差し上げましょう。

なお、C++ を使えば、問題6 は、
次のように簡単に書けたりします。

コード:

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

string s;

void step()
{
	cout << s << endl;
	if (next_permutation(s.begin(), s.end())) step();
}

int main()
{
	int l, r;
	cin >> l >> r;
	s = string(l, 'L') + string(r, 'R');
	step();
}
実行例

コード:

2 3
LLRRR
LRLRR
LRRLR
LRRRL
RLLRR
RLRLR
RLRRL
RRLLR
RRLRL
RRRLL

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#4

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
これらの問題が難しいのは、様々な制約があるからですよね。
・繰り返し文(while や for)を使ってはいけない
・配列を使ってはいけない
・行の長さやデータの個数に上限があってはいけない
など。
これらの制約がなければ、易しいはずです。
問題4

コード:

#include <stdio.h>   // fgets, puts
#include <string.h>  // strchr, strlen

#define K 5

int main(void)
{
	char s[256], *p;
	if (!fgets(s, sizeof s, stdin)) return 1;   // 1行読み込む
	if (p = strchr(s, '\n')) *p = '\0'; // 改行文字を削除
	int i = K, j = strlen(s);           // 比較すべき文字列の末尾
	if (j < 2*K) puts("undecidable");   // 文字数が少ない場合
	else {
		while (i && s[--i] == s[--j]) ; // 比較すべき文字数分、比較する
		puts(i ? "different" : "same"); // 比較すべき文字が残っているか?
	}
}
問題5

コード:

#include <stdio.h>  // scanf, printf, putchar

int main(void)
{
	int i = 0, m = 0, v, a[100];

	while (i < 100 && scanf("%d", &v) == 1 && v >= 0)  // 非負数の読み込み
		a[i++] = v;     // 読み込んだ数の保存
	while (--i >= 0)    // 後ろから処理
		if (a[i] < m) printf("%d ", a[i]); // 後ろの最大値より小さいと表示
		else m = a[i];  // a[i] が最大値なら、最大値の更新
	putchar('\n');
} 
問題6

コード:

#include <stdio.h>   // scanf, puts
#include <string.h>  // memset

int main(void)
{
	char s[8 + 1];  int i, j, k, n, t;

	scanf("%d%d", &i, &j);
	memset(s, 'L', i), memset(s + i, 'R', j);
	n = i + j;
	s[n] = '\0';
	while (1) {
		puts(s);
		for (i = n; --i > 0 && s[i-1] >= s[i]; ) ; // 右端から "LR" を探す
		if (i == 0) return 0;                  // なければ終了
		for (j = i, k = n; ++j < --k; )        // "LR" の右側を反転
			t = s[j], s[j] = s[k], s[k] = t;
		s[i-1] = 'R', s[i] = 'L';              // "LR" を "RL" に置き換える
	}
}
これらは繰り返し文使いまくりなので、解答ではありません。

「ヒントだけでもいいのでください」というので
ヒントを与えているのに、何の応答もありません。
完全な丸投げですね。

問題6 のヒントなんて、ほとんど解答そのものだったのに。

コード:

#include <stdio.h>  // scanf, puts

char s[8 + 1];

void step(int l, int r, int n)
{
	if (l) s[n] = 'L', step(l-1, r, n+1);
	if (r) s[n] = 'R', step(l, r-1, n+1);
	if (l == 0 && r == 0) puts(s);
}

int main(void)
{
	int l, r;
	scanf("%d%d", &l, &r);
	step(l, r, 0);
}

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#5

投稿記事 by かずま » 6年前

問題4で、繰り返し文(while)を使ったコードを示しましたが、
strncmp を使うと、while をなくせます。

コード:

#include <stdio.h>   // fgets, puts
#include <string.h>  // strchr, strlen, strncmp

#define K 5

int main(void)
{
	char s[256], *p;
	if (!fgets(s, sizeof s, stdin)) return 1;   // 1行読み込む
	if (p = strchr(s, '\n')) *p = '\0'; // 改行文字を削除
	int i = strlen(s);
	if (i < 2*K)                        // 文字数が少ない場合
		puts("undecidable");
	else if (strncmp(s, s + i - K, K))  // 先頭と末尾の K文字を比較
		puts("different");
	else
		puts("same");
}
しかし、行の長さに上限があるので、もちろんこれは正解ではありません。

C++ なら上限をなくせます。

コード:

#include <iostream> // cin, cout
#include <string>   // getline
using namespace std;

#define K 5   // または const int K = 5;

int main()
{
	string s;
	if (!getline(cin, s)) return 1;
	if (s.size() < 2*K)
		cout << "undecidable\n";
	else if (s.substr(0, K) != s.substr(s.size() - K))
		cout << "different\n";
	else
		cout << "same\n";
}
問題5 も C++ だと、繰り返し文やデータの個数の上限をなくせます。

コード:

#include <iostream>  // cin, cout, endl
#include <iterator>  // istream_iterator, ostream_iterator, back_inserter
#include <vector>    // vector
#include <algorithm> // copy
using namespace std;

struct In : istream_iterator<int> {
	In() {}
	In(istream& i) : istream_iterator<int>(i) {}
	bool operator!=(const istream_iterator<int>& x) const {
		return **this >= 0 && istream_iterator<int>(*this) != x;
	}
};

struct Out : ostream_iterator<int> {
	int m;
	Out(ostream& o, const char *s) : ostream_iterator<int>(o, s), m(0) { }
	Out& operator=(const int& x) {
		if (x < m) this->ostream_iterator<int>::operator=(x);
		else m = x;
		return *this;
	}
	Out& operator*() { return *this; }
};

int main()
{
	vector<int> a;
	copy(In(cin), In(), back_inserter(a));
	copy(a.rbegin(), a.rend(), Out(cout, " "));
	endl(cout);
}
C では、こんなことができないので、再帰関数を使うんですね。

質問者が何か言ってきたら、再帰版の C のプログラムをお見せできるんですが。

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#6

投稿記事 by かずま » 6年前

問題4で、行の長さの制限をなくしてみましょう。

先頭 K文字と末尾 K文字さえあれば、比較結果を表示できます。
すなわち、行全体を保存する必要はないということです。

コード:

#include <stdio.h>   // getchar, puts

#define K 5

int main(void)
{
	char s1[K + 1], s2[K], s3[K + 1];
	int c, n = 0, m = 0;
	for (; (c = getchar()) != '\n' && c != EOF; n++) {
		if (n < K) s1[n] = c;   // 先頭 K文字だけを保存
		s2[m] = c;              // 末尾 K文字を循環的に保存
		if (++m == K) m = 0;
	}
	if (n < 2*K) puts("decidable");
	else {
		for (n = 0; n < K; n++) {
			s3[n] = s2[m];      // 循環文字列を通常の文字列に変換
			if (++m == K) m = 0;
		}
		s1[K] = s3[K] = '\0';
		if (strcmp(s1, s3)) puts("different");
		else puts("same");
	}
}
しかし、繰り返し文を使っているので、もちろんこれは正解ではありません。
やはり再帰関数の使用が必要です。

質問者が何も応答しないのに、私が返信を繰り返すのは、
これらの問題をとても面白いと思うからです。

K が 5 の場合、"abcdef_g_abcde" という 14文字の入力があると、
s1 には "abcde" が入り、s2 には "bcdea" が入り、
n = 14, m = 4 となります。
"bcde" と "a" を交換するため、s3 という別の配列を使用していますが、
これを s2 だけでできないかと考えてみるのも面白いでしょう。

次のプログラムで、n文字の文字列の先頭 m文字と残りの (n-m)文字を交換
するには、関数 swap をどう書けばいいかという問題です。

コード:

#include <stdio.h>

void swap(char a[], int n, int m)
{
	// ....
}

int main(void)
{
	char a[] = "defghijklmnopqrstuvwxyzabc";
	int n = 26, m = 23;
	swap(a, n, m);
	puts(a);
}

littlestream
記事: 48
登録日時: 8年前

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#7

投稿記事 by littlestream » 6年前

問題4を私も挑戦しましたが、惨敗でした。
不要や無駄のあるプログラミングをしてしまいました。

コード:

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

#define K 6

char *GetFirstNStr(int N,char str[])
{
    str[N]='\0';
    //メモリを破壊しているのでmain関数のstrからstr2にstrcpyする
    
    return str;
}

char *GetLastNStr(int N,char str[])
{
    char str2[100];
    
    int LastKPos=strlen(str)-N;
    //strncpy(str2,str,K);
    //これだとstrの先頭からN個コピーされるので駄目
    
    
    int i=0;
    while(i<N)//i<3
    {
        str2[i]=str[LastKPos+i];
        //printf("str2[%d]=%c\n",i,str2[i]); //for debug
        i++;
    }
    str2[i]='\0';
    
    return str2;
}

int main()
{
    char str[100];
    char str2[100];
    fgets(str,sizeof(str),stdin);
    
    int len=strlen(str);
    str[len-1]='\0';
    
    if(strlen(str)<2*K)
    {
        printf("undecidable\n");
        return 0;
    }
    
    strcpy(str2,str);//メモリが破壊されるので別の変数にコピー
    
    char *FStr;
    char *LStr;
    
    FStr=GetFirstNStr(K,str);//始めN
    LStr=GetLastNStr(K,str2);//ラストN
    
    
    if(strcmp(FStr,LStr)==0) //文字列の比較
         printf("same\n");//同じなら
    else
         printf("different\n");
    
    return 0;
}
コマンドラインの出力結果:
hentaitaihenhentai
same

littlestream
記事: 48
登録日時: 8年前

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#8

投稿記事 by littlestream » 6年前

問題5をやってみましたが、これもあまり出来なかったです。
まずfgetsで一行の文字列を取得しようとして、その文字列の中の空白を,に変えて
sscanfで値を得ようとしたのですが、あまりにも多い数字の場合、sscanfのポインタを
受け取る引数の変数が場合によって変わってしまう為、難しかったので、ここはscanfで
誤魔化しました。
どうすれば場合によって引数の数が変わる関数を作るのかが分からなかったのです。
1つの解決策は関数のC++の関数のオーバーロードだと思ったのですが、今回は見送りました。

コード:

#include<stdio.h>

#define MAX 20

int main()
{
    int tbl[MAX];
    int cnt=0;
    int v=0;
    char s[MAX];
    
    while((v & 0x80000000)!=0x80000000)
    //負数(最上位ビットが1)じゃなければループを続ける
    //負数でint型が4バイトなら8桁の16進数があるはず
    //処理系に依存するやり方なのでオススメは出来ないです
    {
        scanf("%d",&v);
        tbl[cnt]=v;
        cnt++;
    }
    
    int X=1,Y=0;
    
    while(cnt>=0)
    //減算カウンタが0未満にならないようにする
    {
        X=tbl[cnt-1];
        Y=tbl[cnt];
        
        if(X<Y && X!=-1)
        //前と比べつつ、Xが-1じゃない事を確認して出力
        {
            printf("%d",X);
        }
        
        cnt--;
    }
    return 0;
}
コマンドプロンプト出力結果
5
7
8
10
2
4
3
-1
2875

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#9

投稿記事 by かずま » 6年前

littlestream さんが書きました:
6年前
問題4を私も挑戦しましたが、惨敗でした。
不要や無駄のあるプログラミングをしてしまいました。
コンパイラは何を使っていますか?
警告のメッセージが出ませんか?

VC++

コード:

c:\tmp\c\a.c(32) : warning C4172: ローカル変数またはテンポラリのアドレスを返します: str2
gcc -Wall

コード:

a.c: In function 'GetLastNStr':
a.c:32:12: warning: function returns address of local variable [-Wreturn-local-addr]
     return str2;
            ^~~~
次のように printf で変数を見るだけで結果が変わってきませんか?

コード:

    LStr=GetLastNStr(K,str2);//ラストN
    printf("FStr='%s', LStr='%s'\n", FStr, LStr);

    if(strcmp(FStr,LStr)==0) //文字列の比較

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#10

投稿記事 by かずま » 6年前

littlestream さんが書きました:
6年前
問題5をやってみましたが、これもあまり出来なかったです。
そのプログラムでは、
v の次の整数が v より大きいときのみ表示、になっていますよ。
ぶーすと さんが書きました:
6年前
v より後ろに入力された整数の中に v より大きいものがある ときのみ、
v を画面に表示する関数 (配列・ポインタなどは用いない)。
問題5 はこのようになっています。

while の判定ですが、
littlestream さんが書きました:
6年前

コード:

    while((v & 0x80000000)!=0x80000000)
    //負数(最上位ビットが1)じゃなければループを続ける
    //負数でint型が4バイトなら8桁の16進数があるはず
    //処理系に依存するやり方なのでオススメは出来ないです
なぜ次のように素直に書かないのですか?

コード:

    while (v >= 0)

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#11

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
次のプログラムで、n文字の文字列の先頭 m文字と残りの (n-m)文字を交換
するには、関数 swap をどう書けばいいかという問題です。
まずは、余計な配列を使用する方法。

コード:

void swap(char a[], int n, int m)
{
	char b[n];       // 可変長配列は C99 でしか使用できない。
	memcpy(b, a + m, n - m);
	memcpy(b + n - m, a, m);
	memcpy(a, b, n);
}
可変長配列が使えないので、malloc で動的に確保する方法。

コード:

void swap(char a[], int n, int m)
{
	char *b = malloc(n);
	memcpy(b, a + m, n - m);
	memcpy(b + n - m, a, m);
	memcpy(a, b, n);
	free(b);
}
C++ なら、

コード:

void swap(char a[], int n, int m)
{
	std::string b = std::string(a + m, a + n) + std::string(a, a + m);
	std::copy(b.begin(), b.end(), a);
}
余計な配列を使用しない方法。

コード:

#include <stdio.h>

void reverse(char a[], int i, int j)  // a[i] から a[j-1] までを反転
{
	for (char t; i < --j; i++) t = a[i], a[i] = a[j], a[j] = t;
}

void swap(char a[], int n, int m)
{
	reverse(a, 0, m);  // 前半 m文字を反転
	reverse(a, m, n);  // 後半 (n - m)文字を反転
	reverse(a, 0, n);  // 全体を反転
}

int main(void)
{
	char a[] = "defghijklmnopqrstuvwxyzabc";
	int n = 26, m = 23;
	swap(a, n, m);
	puts(a);
}

littlestream
記事: 48
登録日時: 8年前

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#12

投稿記事 by littlestream » 6年前

かずまさんへ

>コンパイラは何を使っていますか?
>警告のメッセージが出ませんか?
今使っているPCがWindows環境ですが、かなり重い(UnityとかVisualStudio2015とか重すぎる)
ので未だにBorlandC++5.5Compilerを使っています。
警告は特に出なかったです。ローカル配列変数を返す関数がまずかったのは
10年前からなんとなく知っていたのですが、何故かは分からなかったです。

>>なぜ次のように素直に書かないのですか?

コード:

while (v >= 0)
これは、未だにファミコンプログラムのアセンブラの癖が
あって、cとアセンブラ、それからVisualBasicの違いに
影響をそれぞれ受けているせいかもしれないです。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#13

投稿記事 by みけCAT » 6年前

問題4
投稿日時が近く、不自然な空白を含めて一字一句同じに近い質問を見つけました。
キーボードから(空白類文字を含むかもしれない)1行を入力して,先頭のK... - Yahoo!知恵袋
ここに答えに近いコードが載っています。

私も解いてみました。
まずはHaskellで。
ただし「キーボードから入力」という所はサボっています。メモリを動的確保しているかもわかりません。

コード:

judge :: String -> String
judge str = judge_i str "" "" (0 :: Integer) (0 :: Integer)
  where
    judge_i s s1 s2 l1 l2 =
      if l1 < k then -- 入力文字列の最初のk文字を取る(逆順になる)
        if null s then "undecidable" -- k文字無かった
        else judge_i (tail s) ((head s) : s1) s2 (succ l1) l2
      else if null s then -- 入力文字列の残りを(逆順で)取ったら
        if l2 < k then "undecidable" -- 文字数が足りているか判定して
        else judge_eq s1 s2 -- 最初のk文字と一致しているか判定する
      else -- 入力文字列の残りを取っていく
        judge_i (tail s) s1 ((head s) : s2) l1 (succ l2)
    judge_eq "" _ = "same"
    judge_eq _ "" = "undecidable"
    judge_eq (s1h:s1t) (s2h:s2t) =
      if s1h == s2h then judge_eq s1t s2t
      else "different"
    k = (5 :: Integer)

main :: IO ()
main = do
  putStrLn $ judge "abcdef_g_abcde"
  putStrLn $ judge "1234567890123456"
  putStrLn $ judge "123456789012345"
  putStrLn $ judge "abcdef"
次に、C言語に移植。

コード:

#include <stdio.h>

#ifndef K
#define K 5
#endif

struct node_t {
	char c;
	struct node_t* next;
};

void judge_eq(struct node_t* s1, struct node_t* s2) {
	if (s1 == NULL) {
		puts("same");
	} else if (s2 == NULL) {
		puts("undecidable");
	} else {
		if (s1->c == s2->c) {
			judge_eq(s1->next, s2->next);
		} else {
			puts("different");
		}
	}
}

void judge(struct node_t* s1, struct node_t* s2, int l1, int l2) {
	struct node_t node;
	int c = getchar();
	if (l1 < K) { /* 入力文字列の最初のk文字を取る(逆順になる) */
		if (c == '\n' || c == EOF) {
			puts("undecidable"); /* k文字無かった */
		} else {
			node.c = c;
			node.next = s1;
			judge(&node, s2, l1 + 1, l2);
		}
	} else if (c == '\n' || c == EOF) { /* 入力文字列の残りを(逆順で)取ったら */
		if (l2 < K) { /* 文字数が足りているか判定して */
			puts("undecidable");
		} else {
			judge_eq(s1, s2); /* 最初のk文字と一致しているか判定する */
		}
	} else { /* 入力文字列の残りを取っていく */
		node.c = c;
		node.next = s2;
		judge(s1, &node, l1, l2 < K ? l2 + 1 : l2);
	}
}

int main(void) {
	judge(NULL, NULL, 0, 0);
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#14

投稿記事 by みけCAT » 6年前

問題5

まずはじめに、
ぶーすと さんが書きました:
6年前
配列・ポインタなどは用いない
この制約は非常に困ります。

まず、「など」の範囲がわかりません。
例えば「配列・ポインタなど(教科書〇〇の△章以降で紹介されている機能)」かもしれないし、
「配列・ポインタなど(この問題の採点者が個人的に嫌いな機能)」かもしれないし、
「配列・ポインタなど(C言語にある言語機能すべて)」かもしれないし…
具体的に何を使ってはいけないのかわからないので、プログラムが書きにくいです。

次に、一般的なパソコン環境においてC言語でポインタを使わずに入出力をするのは難しいと考えられます。
C言語の関数呼び出し演算子()は引数に関数へのポインタを取るので、ポインタを使うことが必要になります。
インラインアセンブラを使ってLinuxのread/writeシステムコールを呼び出す場合、
変数の「アドレス」は使っても「ポインタ」は使わないとみなすことができ、セーフ…なのでしょうか?
しかし、この方法では実行環境が限定されてしまいます。
もちろん、問題文では「C言語」と指定されていないので、他の言語を使うという選択肢もあると考えられます。

さて、ポインタを使って関数を呼び出しているので条件を満たしていないと考えられますが、
とりあえず配列および関数呼び出し以外のポインタを使わずにC言語で実装してみました。
フォーマットの指定も値を読み込む変数の指定もポインタを使うので、printfやscanfは使えないことに注意が必要です。
また、int型の範囲に入らない整数が扱えないだけでなく、
int型の範囲のうちINT_MIN以上-INT_MAX未満の値もうまく扱えない可能性がある不都合があります。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int read_integer(void) {
	int negate = 0;
	int c;
	int result = 0;
	/* 符号か対応する数字が出るまで読み飛ばす */
	do {
		c = getchar();
		if (c == EOF) exit(1);
	} while (!(c == '-' || c == '+' || isdigit(c)));
	/* 符号または最初の一字の処理 */
	if (c == '-') {
		negate = 1;
	} else if (isdigit(c)) {
		result = c - '0';
	}
	/* 残りの文字の処理 */
	while (isdigit(c = getchar())) {
		result = result * 10 + (c - '0'); /* オーバーフローチェックは省略 */
	}
	return negate ? -result : result;
}

void print_integer(int n) {
	if (n < 0) {
		putchar('-');
		print_integer(-n);
	} else {
		if (n > 9) print_integer(n / 10);
		putchar(n % 10 + '0');
	}
}

int process(void) {
	int num = read_integer();
	if (num < 0) {
		return -1;
	} else {
		int max = process();
		if (max < 0 || num >= max) {
			/* 後ろに今の整数より大きい整数は無い */
			return num;
		} else {
			/* 後ろに今の整数より大きい整数がある */
			print_integer(num);
			return max;
		}
	}
}

int main(void) {
	process();
	putchar('\n');
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#15

投稿記事 by かずま » 6年前

問題4

コード:

#include <stdio.h>  // getchar, puts

#define K 5

char s[K];

int step(int n)
{
	int c = getchar();
	if (c == '\n' || c == EOF) return (n < 2*K) ? -2 : K;
	if (n < K) s[n] = c;
	n = step(n + 1);
	return (n <= 0 || s[--n] == c) ? n : -1;
}

int main(void)
{
	switch (step(0)) {
	case -2: puts("undecidable"); break;
	case -1: puts("different"); break;
	default: puts("same");
	}
}
問題5

コード:

#include <stdio.h>

int step(void)
{
	int v, m;
	scanf("%d", &v);
	if (v < 0) return 0;
	m = step();
	if (m <= v) return v;
	printf("%d ", v);
	return m;
}

int main(void)
{
	step();
	putchar('\n');
} 
問題6 は、#4 の最後のプログラムです。

私は、出題者の意図を次のように解釈しました。

配列を使用すると、データの個数に上限が存在してしまう。
不定長のデータを扱うには、リスト構造を使う方法があるが、
「再帰関数」を使用することで不定長のデータが扱えるので、
ポインタの使用と動的確保を禁止する。
配列やリストの処理には、通常繰り返し文を使用するが、
「再帰関数」を使用することで、繰り返し文が不要になるので、
その使用を禁止する。
繰り返し文と同等の処理を記述できる goto文の使用も禁止する。

"%d" のような文字列リテラルが char の配列という型を持ち、
それが先頭要素へのポインタに変換されるという言語仕様を
持ち出して、配列とポインタの使用を制限するのではなく、
あくまでも「処理するデータの保持」について、配列や
ポインタを使用しないこととする。つまり、コード上で、
配列変数やポインタ変数を使用しなければよい。
関数呼出しで、関数が関数へのポインタに変換されるという
言語仕様もポインタの使用とはみなさない。


さて、問題6 の別解として、こんなものを書いてみました。

コード:

#include <stdio.h>   // scanf, puts

#define B3(x) x,x+1,x+1,x+2
#define B2(x) B3(x),B3(x+1),B3(x+1),B3(x+2)
#define B1(x) B2(x),B2(x+1),B2(x+1),B2(x+2)
#define B     B1(0),B1(1),B1(1),B1(2)

#define S3(x) x"RR",x"RL",x"LR",x"LL"
#define S2(x) S3(x"RR"),S3(x"RL"),S3(x"LR"),S3(x"LL")
#define S1(x) S2(x"RR"),S2(x"RL"),S2(x"LR"),S2(x"LL")
#define S     S1("RR"),S1("RL"),S1("LR"),S1("LL")

void step(int n, int r, int i)
{
	static const char b[] = { B }, *s[] = { S };
	if (i < (1 << n)) {
		if (b[i] == r) puts(s[i] + 8 - n);
		step(n, r, i + 1);
	}
}

int main(void)
{
	int l, r;
	scanf("%d%d", &l, &r);
	step(l + r, r, 0);
}

かずま

Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です

#16

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
さて、問題6 の別解として、こんなものを書いてみました。
すみません。L と R 反対でした。次のように訂正します。

コード:

#define S3(x) x"LL",x"LR",x"RL",x"RR"
#define S2(x) S3(x"LL"),S3(x"LR"),S3(x"RL"),S3(x"RR")
#define S1(x) S2(x"LL"),S2(x"LR"),S2(x"RL"),S2(x"RR")
#define S     S1("LL"),S1("LR"),S1("RL"),S1("RR")

返信

“C言語何でも質問掲示板” へ戻る