#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);
}
問題4
[code]
#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");
}
}
[/code]
問題5
[code]
#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');
}
[/code]
問題6 は、#4 の最後のプログラムです。
私は、出題者の意図を次のように解釈しました。
配列を使用すると、データの個数に上限が存在してしまう。
不定長のデータを扱うには、リスト構造を使う方法があるが、
「再帰関数」を使用することで不定長のデータが扱えるので、
ポインタの使用と動的確保を禁止する。
配列やリストの処理には、通常繰り返し文を使用するが、
「再帰関数」を使用することで、繰り返し文が不要になるので、
その使用を禁止する。
繰り返し文と同等の処理を記述できる goto文の使用も禁止する。
"%d" のような文字列リテラルが char の配列という型を持ち、
それが先頭要素へのポインタに変換されるという言語仕様を
持ち出して、配列とポインタの使用を制限するのではなく、
あくまでも「処理するデータの保持」について、配列や
ポインタを使用しないこととする。つまり、コード上で、
配列変数やポインタ変数を使用しなければよい。
関数呼出しで、関数が関数へのポインタに変換されるという
言語仕様もポインタの使用とはみなさない。
さて、問題6 の別解として、こんなものを書いてみました。
[code]
#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);
}
[/code]