20111130

小強歷險記

題目出自DS課本第二章,滿有意思的一題。

題目

一隻喝醉酒的小強在地板上爬來爬去,
假設地板面積為row * col。
假設小強的起始位置為座標(i, j),
小強一次可以移動一格。
小強可以從他現在所待的格子,
移動到周遭其他格子,且機率相等。
請問小強爬過每塊面積至少一次,
需要花多久時間?
輸出總步數與每塊格子經過的次數。

分析

算是一題模擬題。
首先要知道地板的大小,
意即maxrow跟maxcol要定出來。
接著要知道起始點row, col。
從起始點開始往周遭移動,
我們還需要隨機函式與移動方式。
小強從自身待的格子移動到周遭至多有八格。
(八格就是八種情況)
需要考慮邊界問題,與上界問題。
邊界表示,小強移動只能在地板中,不能移動到地板之外;
上界表示,一個格子至多經過x次,超過x次就不能再進入格子。
(這個動作確保有解,程式才得以終止。)
課本提供一種很好的移動資料結構:利用陣列。
假設移動的row座標為mover,
移動的col座標為movec。
小強可以移動到左上,上,右上,右,右下,下,左下,左。
看出來了嗎!
一個點對應一組mover跟movec。
所以mover與movec的內容分別是:
mover[8] = {1, 1, 1, 0, -1, -1, -1, 0};
movec[8] = {-1, 0, 1, 1, 1, 0, -1, -1};
至於要怎麼移動呢?
row = i + mover[j]
col = j + movec[j]
j從0到7為止。
如此配合隨機函式,可以隨機得到一個數字,
介於0到7,得到row與col後,再考慮邊界問題,

若在編界之內,且經過該格子的次數小於x就移動到(row, col)。

到小強每塊格子都經過至少一次時,
就可以輸出總步數與格子的經過情況數。

如果可以用顏色深淺代表移動次數那一定更好玩 XD。

實做程式碼

/* DS ch 2.9 exercise 9
 * mythnc
 * 2011/11/29 10:56:04   
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW   40
#define COL   20
#define FALSE 0
#define TRUE  1
#define MAXT  50000

typedef struct point {
    int x;
    int y;
} Point;

void move(Point, int (*)[], int, int);
int terminal(int (*)[], int, int);
void printout(int (*)[], int, int);

int totalmove = 1;

int main(void)
{
    int square[ROW][COL] = { 0 };
    int maxrow, maxcol;
    Point start;

    printf("max row and col: ");
    scanf("%d %d", &maxrow, &maxcol);
    getchar();
    printf("start point (x, y): ");
    scanf("%d %d", &start.x, &start.y);
    getchar();

    move(start, square, maxrow, maxcol);

    system("clear");
    printout(square, maxrow, maxcol);

    return 0;
}

/* move: cockroach move simulation */
void move(Point start, int (*s)[COL], int maxr, int maxc)
{
    int xmove[] = {1, 1, 1, 0, -1, -1, -1, 0};
    int ymove[] = {-1, 0, 1, 1, 1, 0, -1, -1};
    int i, x, y;

    srand((unsigned)time(NULL));
    s[start.x][start.y]++;
    while (!terminal(s, maxr, maxc)) {
        system("clear");
        printout(s, maxr, maxc);
        getchar();
        i = rand() % 8;
        x = start.x + xmove[i];
        y = start.y + ymove[i];
        if (x >= 0 && x < maxr && y >= 0 && y < maxc
            && s[x][y] <= MAXT) {
            s[x][y]++;
            totalmove++;
            start.x = x;
            start.y = y;
        }
    }
}

/* terminal: terminal contidion */
int terminal(int (*s)[COL], int maxr, int maxc)
{
    int i, j;

    for (i = 0; i < maxr; i++)
        for (j = 0; j < maxc; j++)
            if (s[i][j] == 0)
                return FALSE;

    return TRUE;
}

/* printout: print out result */
void printout(int (*s)[COL], int maxr, int maxc)
{
    int i, j;

    printf("\ntotal move: %d\n", totalmove);
    printf("count array:\n\n");
    for (i = 0; i < maxr; i++) {
        printf("%5d", s[i][0]);
        for (j = 1; j < maxc; j++)
            printf(" %5d", s[i][j]);
        putchar('\n');
    }
    putchar('\n');
}

ACM 371 Ackermann Functions

ACM100類似題。
兩個白爛點:
(1) 1的次數為3不為1 -.-。
(2) 未必是L H的input形式,有可能是H L。
但output還是要用L H的形式。 =.=

有夠白爛。
像這種定義有問題或是input/output不明確的題目都是爛題目。

/* ACM 371 Ackermann Functions
 * mythnc
 * 2011/11/30 08:23:02
 * run time: 0.128
 */
#include <stdio.h>

#define SWAP(X, Y, T) T = X, X = Y, Y = T

typedef struct max {
    int index, value;
} Max;

Max cal(int, int);
int count(long long);

int main(void)
{
    int i, j, tmp;
    Max m;

    while (scanf("%d %d", &i, &j) && i != 0) {
        if (i > j)
            SWAP(i, j, tmp);
        m = cal(i, j);
        printf("Between %d and %d, %d generates the longest sequence of %d values.\n",
               i, j, m.index, m.value);
    }

    return 0;
}

/* cal: find the big cycle len, and return it */
Max cal(int small, int big)
{
    int i, tmp;
    Max m;

    m.index = small;
    m.value = count(small);
    for (i = small + 1; i <= big && i > 0; i++) {
        tmp = count(i);
        if (m.value < tmp) {
            m.index = i;
            m.value = tmp;
        }
    }

    return m;
}

/* count: count the numbers of value n to 1 */
int count(long long n)
{
    int c;

    if (n == 1)
        return 3;

    for (c = 0; n != 1; c++)
        if (n % 2 == 0)
            n >>= 1;    /* n /= 2 */
        else 
            n = n * 3 + 1;

    return c;
}

20111129

ACM 439 Knight Moves

想好久的題目。
後來DS看完array了,
剛好exercise有一題也是knight move,
想說做完這題習題應該就能解這題ACM了,
結果悲劇,根本是不一樣的問題。
習題的題目:隨便給一個點,繞完西洋棋上每個點剛剛好一次。
這題:給兩點,由A點到B點的最短移動次數。

後來想到一種方法,不如從起始點A開始填數字(數字表示移動次數),
把西洋棋盤填滿,這樣就能知道A到B的移動次數。
但是有個問題,不知道能不能依序移動。
(後來看別人的作法,是可以依序移動)
依序移動的意思是,從某個點開始1, 2, 3一直往上填。
所以作法上保守一點,
就從A點開始,填滿所有的1,再從每個1中依序填滿所有的2,以此類推。
每填好一個,就丟到stack中,當所有的1都丟到stack中後,
最先丟進去的1要先拿出來用。依此類推。
有點first in first out的感覺。
應該叫quene不叫stack。

/* ACM 439 Knight Moves
 * mythnc
 * 2011/11/29 16:49:26   
 * run time: 0.024
 */
#include <stdio.h>
#include <string.h>

#define MAXCHAR 3  /* 2 + '\0' */
#define MAXS    8
#define RANGE(X) X >= 0 && X < MAXS

typedef struct point {
    int x; /* row */
    int y; /* col */
} Point;

Point stoc(char *);
void draw(int (*)[], Point);
void filln(int (*)[], Point, Point);

int count;
Point stack[MAXS * MAXS];

int main(void)
{
    char start[MAXCHAR], end[MAXCHAR];
    int square[MAXS][MAXS];
    Point from, to;

    while (scanf("%s %s", start, end) == 2) {
        /* init */
        memset(square, 0, sizeof(square));

        from = stoc(start);
        to = stoc(end);
        draw(square, from);

        printf("To get from %s to %s takes %d knight moves.\n",
                start, end, square[to.x][to.y]);
    }

    return 0;
}

/* stoc: string change to coordinate */
Point stoc(char *s)
{
    Point pt;
    pt.x = s[1] - '1';
    pt.y = s[0] - 'a';

    return pt;
}

/* draw: fill square with numbers */
void draw(int (*s)[MAXS], Point pt)
{
    int i, j;
    Point origin;

    origin.x = pt.x;
    origin.y = pt.y;
    count = 0;
    filln(s, pt, origin);

    for (i = 0; i < count; i++) {
        pt.x = stack[i].x;
        pt.y = stack[i].y;
        filln(s, pt, origin);
    }
}

/* filln: fill blocks to number n */
void filln(int (*s)[MAXS], Point pt, Point o)
{
    int i, x, y, n;
    int movex[] = {-2, -1, 1, 2, 2, 1, -1, -2};
    int movey[] = {1, 2, 2, 1, -1, -2, -2, -1};

    n = s[pt.x][pt.y] + 1;
    for (i = 0; i < 8; i++) {
        x = pt.x + movex[i];
        y = pt.y + movey[i];
        if (RANGE(x) && RANGE(y) && s[x][y] == 0
            && (x != o.x || y != o.y)) {
            s[x][y] = n;
            stack[count].x = x;
            stack[count].y = y;
            count++;
        }
    }
}

20111128

ACM 446 Kibbles `n' Bits `n' Bits `n' Bits

做進位轉換。

/* ACM 446 Kibbles `n' Bits `n' Bits `n' Bits
 * mythnc
 * 2011/11/28 10:07:08   
 * run time: 0.004
 */
#include <stdio.h>
#include <string.h>

#define MAXB  14
#define MAXH  4
#define OPR   2

int hextodec(char *);
int atod(char);
void dectobin(int, char *);
void reverse(char *);
int result(int, int, char *);

int main(void)
{
    char opd[2][MAXH];
    char opr[OPR];
    char b[2][MAXB];
    int d[2];
    int i;

    scanf("%*d");
    while (scanf("%s %s %s", opd[0], opr, opd[1]) == 3) {
        for (i = 0; i < 2; i++) {
            d[i] = hextodec(opd[i]);
            dectobin(d[i], b[i]);
            reverse(b[i]);
        }
        printf("%s %s %s = %d\n", b[0], opr, b[1], result(d[0], d[1], opr));
    }

    return 0;
}

/* hextodec: hex number change to decimal number */
int hextodec(char *s)
{
    int i, n, mul;

    mul = 1;
    for (n = 0, i = strlen(s) - 1; i > -1; i--) {
        n += mul * atod(s[i]);
        mul *= 16;
    }

    return n;
}

/* atoh: convert ascii code to decimal number */
int atod(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';

    return c - 'A' + 10;
}

/* dectobin: decimal number change to binary form */
void dectobin(int d, char *b)
{
    int i;

    for (i = 0; d != 0; i++) {
        b[i] = d % 2 + '0';
        d /= 2;
    }
    for (; i < MAXB - 1; i++)
        b[i] = '0';
    b[i] = '\0';
}

/* reverse: reverse b */
void reverse(char *b)
{
    int i, j;
    char tmp;

    for (i = 0, j = strlen(b) - 1; i < j; i++, j--) {
        tmp = b[i];
        b[i] = b[j];
        b[j] = tmp;
    }
}

/* result: depend on opr, do d1 + d2,
 * or d1 - d2 */
int result(int d1, int d2, char *opr)
{
    if (strcmp(opr, "+") == 0)
        return d1 + d2;

    return d1 - d2;
}

C語言做四捨五入

四捨五入如果知其道理,其實並沒有很難做。
把握一個原則:浮點數轉成整數,小數部位會全部捨去。
所以最簡單的作法就是,把浮點數加0.5,再轉成整數即可。
因為四以下要捨,五以上要入,所以加0.5。如果是負數,
則減去0.5。若是做四捨五入到小數第一位的話,就先乘10,
再加0.5,再除以10(唯要注意型態轉換),以此類推。

其實math.h中就有round()函式,但是是C99的東西。
或是用floor()跟ceil()實做也是ok的。

以下是程式碼。(寫個大概而已)

#include <stdio.h>

int round(double);

int main(void)
{
    double x;

    scanf("%lf", &x);
    printf("%d\n", round(x));

    return 0;
}

/* round: do round to x */
int round(double x)
{
    if (x > 0)
        return (int)(x + 0.5);

    return (int)(x - 0.5);
}




參考連結:
http://dejob.blogspot.com/2009/01/c-rounding.html
http://blog.xuite.net/freeleo168/Learn/19008875