20111231

ACM 10281 Average Speed

單純的距離 = 時間差 * 速率。
每次求出新的時間後就代替舊有時間,
如此時間差可以馬上求得。
input用fgets先吃,再用sscanf抓資料。
有三行重複……感覺很醜,但是位置也不能改啊 orz。

/* ACM 10281 Average Speed
 * mythnc
 * 2011/12/31 08:12:03   
 * run time: 0.008
 */
#include <stdio.h>

#define LINEMAX 20

int main(void)
{
    char line[LINEMAX];
    int h, m, s, t1, t2;
    int newv, v;
    double dis;

    h = m = s = t1 = v = 0;
    dis = 0.0;
    while (fgets(line, LINEMAX, stdin))
        if (sscanf(line, "%d:%d:%d %d", &h, &m, &s, &newv) == 4) {
            t2 = h * 3600 + m * 60 + s;
            dis += (double)((t2 - t1) * v) / 3600.0;
            t1 = t2;
            v = newv;
        }
        else if (sscanf(line, "%d:%d:%d", &h, &m, &s) == 3) {
            t2 = h * 3600 + m * 60 + s;
            dis += (double)((t2 - t1) * v) / 3600.0;
            t1 = t2;
            /* output */
            printf("%02d:%02d:%02d %.2f km\n", h, m, s, dis);
        }

    return 0;
}

20111230

ACM 900 Brick Wall Patterns

觀察n值與排列方式。
事實上可以發現有直板跟橫板兩種情況。
直板一個佔用一個面積,橫板一個佔用兩個面積。
所以n = 1時,無橫板,
n = 2時,最多1個橫板,
n = 3時,最多1個橫板,
n = 4時,最多2個橫板,
n = 5時,最多2個橫板,
n = 6時,最多3個橫板。
可以發現最多橫板數為n / 2。
每多一個橫板,組合方式就會多一種。
n = 1時,無橫板的組合,
n = 2時,無橫板的組合 + 1個橫板的組合,
n = 3時,無橫板的組合 + 1個橫板的組合,
n = 4時,無橫板的組合 + 1個橫板的組合 + 2個橫板的組合,
n = 5時,無橫板的組合 + 1個橫板的組合 + 2個橫板的組合,
n = 6時,無橫板的組合 + 1個橫板的組合 + 2個橫板的組合 + 3個橫板的組合。
無橫板的組合,就都是直板:Cn取n。
1個橫板的組合,為n - 2個直板 + 1個橫板:C的n - 1取1。
2個橫板的組合,為n - 4個直板 + 2個橫板:C的n - 2取2。
看出來了嗎?
每多一塊橫板,直板的數目就會少2。
有了直板與橫板的數目,就可以求得組合的情況。
其實就是要算這種問題:
有m塊直板跟n塊橫板,求其組合方式:(m + n)! / (n! * m!)。
把組合數加一加,就是答案了。
可以利用ACM369的方式計算組合。

/* ACM 900 Brick Wall Patterns
 * mythnc
 * 2011/12/30 10:12:43   
 * run time: 0.008
 */
#include <stdio.h>

#define MAXN 50
#define MIN(X, Y) ((X <= Y) ? X : Y)

long long count(int);
long long com(int, int);
int gcd(int, int);

int main(void)
{
    int n, i;

    while (scanf("%d", &n) && n != 0)
        printf("%lld\n", count(n));

    return 0;
}

/* count: return the combination numbers */
long long count(int n)
{
    int i, ver, hor;
    long long times;

    times = 1;
    ver = n - 2;
    hor = 1;
    while (ver >= 0) {
        times += com(ver, hor);
        ver -= 2;
        hor++;
    }

    return times;
}

/* com: return the combination times:
 * (m + n)! / (n! * m!) */
long long com(int m, int n)
{
    int sum, i, j, times, factor, tmp;
    long long ans[MAXN] = { 0 };

    if (m == 0 || n == 0)
        return 1;
    if (m == 1 || n == 1)
        return m + n;

    times = 0;
    sum = m + n;
    /* init */
    for (i = sum - MIN(m, n) + 1; i <= sum; i++)
        ans[times++] = i;
    /* simplify */
    for (i = 2; i <= MIN(m, n); i++) {
        tmp = i;
        for (j = 0; j < times; j++) {
            if (ans[j] % tmp == 0) {
                ans[j] /= tmp;
                break;
            }
            if ((factor = gcd(tmp, ans[j])) != 1) {
                ans[j] /= factor;
                tmp /= factor;
            }
        }
    }
    /* cal ans */
    for (i = 1; i < times; i++)
        ans[0] *= ans[i];

    return ans[0];
}

/* gcd: return gcd */
int gcd(int a, int b)
{
    while ((a %= b) && (b %= a))
        ;

    return a + b;
}

20111229

ACM 10699 Count the factors

找質因數個數。

每次找到一個因數p後,就讓n = n / p,
再繼續對n做找因數的動作。
如此計算因數總數即為答案。

先建一個質數表再做會比較快。

/* ACM 10699 Count the factors
 * mythnc
 * 2011/12/29 09:41:28   
 * run time: 0.024
 */
#include <stdio.h>

int countp(int);

int main(void)
{
    int n;

    while (scanf("%d", &n) && n != 0)
        printf("%d : %d\n", n, countp(n));

    return 0;
}

/* countp: return the number of prime factor */
int countp(int n)
{
    int i, count;

    for (i = 2, count = 0; n != 1; i++) {
        if (n % i == 0) {
            count++;
            while (n % i == 0)
                n /= i;
        }
    }

    return count;
}

20111228

ACM 10931 Parity

邊轉成bin邊找1的個數即可。

/* ACM 10931 Parity
 * mythnc
 * 2011/12/28 08:14:34   
 * run time: 0.004
 */
#include <stdio.h>
#include <string.h>

#define MAXCHAR 32

int dectobin(int, char *);
void reverse(char *);

int main(void)
{
    int n, count;
    char bin[MAXCHAR];

    while (scanf("%d", &n) && n != 0) {
        count = dectobin(n, bin);
        reverse(bin);
        printf("The parity of %s is %d (mod 2).\n", bin, count);
    }
        
    return 0;
}

/* dectobin: make decimal number to
 * correspond its binary number,
 * and return the number of 1(bin) */
int dectobin(int n, char *b)
{
    int i, count;

    i = count = 0;
    while (n) {
        if (n % 2)
            count++;
        b[i++] = n % 2 + '0';
        n /= 2;
    }
    b[i] = '\0';

    return count;
}

/* reverse: reverse bin number */
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;
    }
}

20111227

ACM 10004 Bicoloring

第一次寫graph,好刺激……

一開始想說沒這麼難吧?不就填色嗎!
填完答案就出來了!
不過像input為
1 2
3 4
2 3
就會gg了。
所以一定要是traversal + 填色兩個動作。
光是填或是光是traversal是不行的。

填色 + DFS檢查填色狀況。
利用adjacency list建graph。
之後從graph[0]開始填色(填在每個graph[i]),
填完每個跟graph[0]相連的node之後,做DFS走訪graph,
一樣先做填色的動作,再做DFS。
若填色時發現該node顏色與要填的顏色不一,
表示沒有bicolor的形式。
若能順利填完色走訪完整個graph,表示有bicolor的形式。

總覺得寫得很髒 orz,不知道有沒有其他比較好的方式,
填色O(e)感覺花了不少時間。

這個問題其實是一個Bipartite Graph。
解了Bipartite Graph這題就能解了,這題能解了Bipartite Graph也可以解了。

/* ACM 10004 Bicoloring
 * mythnc
 * 2011/12/26 19:44:34   
 * run time: 0.012
 */
#include <stdio.h>
#include <stdlib.h>

#define MAXNODE 199
#define TRUE    1
#define FALSE   0

enum color {none, white, black};

typedef struct node {
    int n;
    enum color c;
    struct node *next;
} Node;

void init(Node **);
void input(Node **, int, int);
int check(Node **, int);
void freeg(Node **, int);

int visited[MAXNODE];

int main(void)
{
    int n, e, u, v, i;
    Node *graph[MAXNODE];

    while (scanf("%d", &n) && n != 0) {
        scanf("%d", &e);
        init(graph);
        for (i = 0; i < e; i++) {
            scanf("%d %d", &v, &u);
            input(graph, u, v);
            input(graph, v, u);
        }
        if (check(graph, 0))
            printf("BICOLORABLE.\n");
        else
            printf("NOT BICOLORABLE.\n");
        freeg(graph, n);
    }

    return 0;
}

/* init: initialize graph node to NULL and visited to zero */
void init(Node **graph)
{
    int i;

    for (i = 0; i < MAXNODE; i++) {
        graph[i] = NULL;
        visited[i] = FALSE;
    }
}

/* input: receive input data */
void input(Node **graph, int u, int v)
{
    Node *tmp, *pt;

    tmp = (Node *)malloc(sizeof(Node));
    tmp->n = u;
    tmp->c = none;
    tmp->next = NULL;
    if (!graph[v]) {
        graph[v] = (Node *)malloc(sizeof(Node));
        graph[v]->n = v;
        graph[v]->c = none;
        graph[v]->next = tmp;
    }
    else {
        pt = graph[v];
        while (pt->next)
            pt = pt->next;
        pt->next = tmp;
    }
}

/* check(dfs): check each node and
 * its adjacency are different colors.
 * if have same color stop dfs and return FALSE
 * else do dfs to the end and return TRUE */
int check(Node **graph, int n)
{
    enum color c;
    Node *pt;

    pt = graph[n];
    visited[n] = TRUE;
    if (pt->c == none) {
        pt->c = white;
        c = black;
    }
    else if (pt->c == white)
        c = black;
    else
        c = white;
    for (pt = pt->next; pt; pt = pt->next)
        if (graph[pt->n]->c == none)
            graph[pt->n]->c = c;
        else if (graph[pt->n]->c == c)
            ;
        else
            return FALSE;
    for (pt = graph[n]->next; pt; pt = pt->next)
        if (!visited[pt->n])
            if (!check(graph, pt->n))
                return FALSE;

    return TRUE;
}

/* freeg: free link node */
void freeg(Node **graph, int n)
{
    int i;
    Node *pt, *tmp;

    for (i = 0; i < n; i++) {
        pt = graph[i];
        while (pt) {
            tmp = pt;
            pt = pt->next;
            free(tmp);
        }
    }
}

20111226

ACM 10499 The Land of Justice

寫這麼多,其實只要知道,價錢是依照表面積來算即可。

球的表面積為4 * pi * r ^ 2,
球切成n塊的表面積為(1 / n)的球表面積 + 2塊(1 / 2)的圓面積,
所以n塊的總表面積為4 * pi * r ^ 2 + n * pi * r ^ 2。

利潤為(賣價 - 買價) / 買價。
[4 * pi * r ^ 2 + n * pi * r ^ 2 - 4 * pi * r ^ 2] / 4 * pi * r ^ 2,
=> n * pi * r ^ 2 / 4 * pi * r ^ 2,
=> n / 4。

乘上100,
=> 25n %,即為答案。

注意若n = 1為利潤為0%。

/* ACM 10499 The Land of Justice
 * mythnc
 * 2011/12/26 10:04:46   
 * run time: 0.024
 */
#include <stdio.h>

int main(void)
{
    long long n;

    while (scanf("%lld", &n) && n > 0)
        if (n == 1)
            printf("0%%\n");
        else
            printf("%lld%%\n", n * 25);

    return 0;
}

20111225

ACM 156 Ananagrams

一邊吃token,一邊判斷該token與已存的資料是否有anagram的現象,
若有,就不存,並把現有的token做tag;
若無,存起來。
吃完token後,把沒tag的資料做sort,
再output即可。

用linked list解,
練習ll的一些函式實作。

/* ACM 156 Ananagrams
 * mythnc
 * 2011/12/25 20:30:50   
 * run time: 0.008
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXL  21
#define FALSE 0
#define TRUE  1

typedef struct token {
    int tag, len;
    char word[MAXL];
    struct token *next;
} Token;

Token *input(void);
int ananagrams(Token *, char *);
int sameletter(char *, char *);
void *deletetag(Token **);
void *sort(Token **);
void output(Token *);
void freel(Token *);

int main(void)
{
    Token *head;

    head = input();
    deletetag(&head);
    sort(&head);
    output(head);
    freel(head);

    return 0;
}

/* input: eat input token */
Token *input(void)
{
    char word[MAXL];
    Token *tmp, *head, *current;

    head = current = NULL;
    while (scanf("%s", word) && word[0] != '#') {
        if (head && !ananagrams(head, word))
            continue;
        tmp = (Token *)malloc(sizeof(Token));
        if (!head)
            head = tmp;
        strcpy(tmp->word, word);
        tmp->len = strlen(word);
        tmp->tag = FALSE;
        tmp->next = NULL;
        if (current)
            current->next = tmp;
        current = tmp;
    }

    return head;
}

/* ananagrams: compare word in ll vs w,
 * if they are anagrams return FALSE
 * else return TRUE */
int ananagrams(Token *pt, char *w)
{
    while (pt) {
        if (pt->len == strlen(w) && sameletter(pt->word, w)) {
            pt->tag = TRUE;
            return FALSE;
        }
        pt = pt->next;
    }

    return TRUE;
}

/* sameletter: if s and t have same letter
 * return TRUE else return FALSE */
int sameletter(char *s, char *t)
{
    int letters[26] = { 0 };
    int i;

    for (i = 0; i < strlen(s); i++)
        if (s[i] >= 'A' && s[i] <= 'Z')
            letters[s[i] - 'A']++;
        /* lowercase */
        else
            letters[s[i] - 'a']++;

    for (i = 0; i < strlen(t); i++)
        if (t[i] >= 'A' && t[i] <= 'Z') {
            if (letters[t[i] - 'A'] == 0)
                return FALSE;
            letters[t[i] - 'A']--;
        }
        /* lowercase */
        else {
            if (letters[t[i] - 'a'] == 0)
                return FALSE;
            letters[t[i] - 'a']--;
        }

    return TRUE;
}

/* deletetag: if tag == TRUE, delete it */
void *deletetag(Token **head)
{
    Token *pre, *pt, *tmp;

    pre = NULL;
    pt = *head;
    while (pt) {
        if (pt->tag) {
            if (!pre) {
                tmp = *head;
                *head = (*head)->next;
            }
            else {
                tmp = pt;
                pre->next = pt->next;
            }
            free(tmp);
            pt = pt->next;
            continue;
        }
        pre = pt;
        pt = pt->next;
    }
}

/* sort: insertion sort linked list */
void *sort(Token **head)
{
    Token *i, *j, *pre, *tmpi, *tmpj;

    for (i = (*head)->next; i; i = i->next) {
        tmpi = i;
        for (j = *head, pre = NULL; j != tmpi; pre = j, j = j->next)
            if (strcmp(tmpi->word, j->word) < 0) {
                if (pre)
                    pre->next = tmpi;
                else
                    *head = tmpi;
                tmpj = j;
                while (tmpj->next != tmpi)
                    tmpj = tmpj->next;
                tmpj->next = tmpi->next;
                tmpi->next = j;
                break;
            }
    }
}

/* output: output result */
void output(Token *pt)
{
    for (; pt; pt = pt->next)
        printf("%s\n", pt->word);
}

/* free: free linked list */
void freel(Token *pt)
{
    Token *tmp;

    while (pt) {
        tmp = pt;
        free(tmp);
        pt = pt->next;
    }
}

20111223

ACM 10432 Polygon Inside A Circle

n > 2,必定有三點,
任意兩點加上圓心會構成一個三角形,
共有n個三角形。
求出三角形的面積再乘以n即為答案。

已知三角形為等腰三角形,從底邊中間切一半,
可以得到兩塊一模一樣的直角三角形。
斜邊為R,對邊跟鄰邊都不知道。
斜邊與鄰邊的夾角a為360 / (n * 2)。
利用三角函數,可知對邊與鄰邊為sin(a) * R和cos(a) * R。
如此可求出此三角形面積:(1 / 2) * sin(a) * R * cos(a) * R。
一個等腰三角形由兩個直角三角形構成,n邊形由n個等腰三角形構成,
所以n邊形面積為n * 2 * (1 / 2) * sin(a) * R * cos(a) * R,
化簡後可得:n * sin(a) * R * cos(a) * R。

利用math.h的函式sin與cos即可求解。
但要先把度換成弧度(徑度)。
pi = 180度,
所以徑度 = pi / 180。
(pi / 180) * [360 / (n * 2)],
化簡後為pi / n。

r可能為浮點數,要用double吃。

/* ACM 10432 Polygon Inside A Circle
 * mythnc
 * 2011/12/23 14:04:53   
 * run time: 0.008
 */
#include <stdio.h>
#include <math.h>

#define PI 3.141592653589793

int main(void)
{
    int n;
    double r;

    while (scanf("%lf %d", &r, &n) == 2)
        printf("%.3f\n", n * r * sin(PI / n) * r * cos(PI / n));

    return 0;
}

20111221

ACM 392 Polynomial Showdown

需要考慮的:
首項,係數(0、1、其他),次方(0、1、其他)、正負號。
只有0的情況要額外考慮。

/* ACM 392 Polynomial Showdown
 * mythnc
 * 2011/12/21 09:15:20   
 * run time: 0.108
 */
#include <stdio.h>
#include <string.h>

#define MAXD  9
#define TRUE  1
#define FALSE 0

void output(int *);

int main(void)
{
    int i;
    int poly[MAXD] = { 0 };

    while (scanf("%d", &poly[8]) == 1) {
        for (i = 7; i > -1; i--)
            scanf("%d", &poly[i]);
        output(poly);
        /* init */
        memset(poly, 0, sizeof(int) * MAXD);
    }

    return 0;
}

void output(int *p)
{
    int i, first;

    for (i = 8, first = TRUE; i > -1; i--) {
        if (p[i] == 0)
            continue;
        /* first element */
        if (first) {
            first = FALSE;
            if (p[i] < 0)
                putchar('-');
        }
        else if (p[i] > 0)
            printf(" + ");
        else
            printf(" - ");
        /* coefficient */
        if (p[i] > 1 && i != 0)
            printf("%d", p[i]);
        else if (p[i] < -1 && i != 0)
            printf("%d", -p[i]);
        /* degree */
        if (i > 1)
            printf("x^%d", i);
        else if (i == 1)
            putchar('x');
        else
            printf("%d", p[i] >= 0 ? p[i] : -p[i]);
    }
    if (first)
        putchar('0');
    putchar('\n');
}

20111220

ACM 10473 Simple Base Conversion

hex to dec,用%x吃,%d輸出;
dec to hex,用%d吃,%X輸出。

/* ACM 10473 Simple Base Conversion
 * mythnc
 * 2011/12/20 10:18:59   
 * run time: 0.016
 */
#include <stdio.h>

#define MAXCHAR 11

int main(void)
{
    char s[MAXCHAR];
    int n;

    while (scanf("%s", s) == 1) {
        /* hex to dec */
        if (s[1] == 'x') {
            sscanf(s, "%x", &n);
            printf("%d\n", n);
            continue;
        }
        /* dec to hex */
        sscanf(s, "%d", &n);
        if (n < 0)
            break;
        printf("0x%X\n", n);
    }

    return 0;
}

20111219

ACM 444 Encoder and Decoder

苦工題。
ACM713有點像。
encode時,先做mapping,之後做reverse;
decode時,先做reverse,再做mapping。

另外題目騙人,input最長並非80個char,
所以如果WA的話,就把array設為1000吧。
ACM真愛騙人。

/* ACM 444 Encoder and Decoder
 * mythnc
 * 2011/12/19 17:20:43   
 * run time: 0.004
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXN    123
#define MAXCHAR 4
#define LINEMAX 1000
#define OTHERSC 7

void map(char (*)[]);
void itoa(char *, int);
void reverse(char *);
void decode(char *, char *);
void encode(char *, char (*)[], char *);

int main(void)
{
    char key[MAXN][MAXCHAR];
    char input[LINEMAX];
    char output[LINEMAX * 3];

    map(key);

    while (fgets(input, LINEMAX, stdin)) {
        /* eat newline */
        input[strlen(input) - 1] = '\0';
        /* number: decode it */
        if (input[0] >= '0' && input[0] <= '9')
            decode(input, output);
        /* encode it */
        else
            encode(input, key, output);
        /* output */
        printf("%s\n", output);
    }

    return 0;
}

/* map: map char to int */
void map(char (*key)[MAXCHAR])
{
    char tmp[MAXCHAR];
    int trans[] = {32, 33, 44, 46, 58, 59, 63};
    int i;

    /* uppercase letter */
    for (i = 65; i <= 90; i++) {
        itoa(tmp, i);
        strcpy(key[i], tmp);
    }
    /* lowercase letter */
    for (i = 97; i <= 122; i++) {
        itoa(tmp, i);
        strcpy(key[i], tmp);
    }
    /* others chars */
    for (i = 0; i < OTHERSC; i++) {
        itoa(tmp, trans[i]);
        strcpy(key[trans[i]], tmp);
    }
}

/* itoa: int to ascii */
void itoa(char *tmp, int n)
{
    int i;

    i = 0;
    while (n != 0) {
        tmp[i++] = n % 10 + '0';
        n /= 10;
    }
    tmp[i] = '\0';
    reverse(tmp);
}

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

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

/* decode: decode number to ascii code */
void decode(char *s, char *output)
{
    int i, count;
    char tmp[MAXCHAR];

    reverse(s);
    for (i = count = 0; i < strlen(s); ) {
        if (s[i] == '1') {
            strncpy(tmp, s + i, 3);
            tmp[3] = '\0';
            i += 3;
        }
        else {
            strncpy(tmp, s + i, 2);
            tmp[2] = '\0';
            i += 2;
        }
        output[count++] = atoi(tmp);
    }
    output[count] = '\0';
}

/* encode: encode char to number */
void encode(char *s, char (*key)[MAXCHAR], char *output)
{
    char *pt;
    int i;

    output[0] = '\0';
    pt = output;
    for (i = 0; i < strlen(s); i++) {
        pt = strcat(pt, key[(int)s[i]]);
    }
    reverse(output);
}

20111218

ACM 112 Tree Summing

input轉tree想超久的,
做出來還是很happy。
(一次就AC超爽的)
解法很正統……
先建tree,之後才做traversal。
(1)建tree
因為()會是pair,所以先找到「(」即可丟入addtree()中,
當找到「)」後就return。
利用recursive就能建立完成。
(2)traversal
做preorder traversal,
利用stack紀錄每個node的value。
到leaf node時做sum,把sum存起來。

最後判斷input值是否與sum相同即可。

看到別人的解法是可以在建tree的同時做sum,
這樣之後就不用作traversal,會比較快。

/* ACM 112 Tree Summing
 * mythnc
 * 2011/12/18 19:06:31   
 * run time: 0.064
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXCHAR 11
#define MAXN    1000
#define TRUE    1
#define FALSE   0

typedef struct node {
    char value[MAXCHAR];
    struct node *left, *right;
} Node;

Node *addtree(void);
void traversal(Node *, int *, int *);
int contain(int, int *, int);
void freet(Node *);

int stack[MAXN];
int stc;

int main(void)
{
    Node *root;
    int n, c, count;
    int sumall[MAXN];

    while (scanf("%d", &n) == 1) {
        /* find the 1st '(' */
        while ((c = getchar()) != '(')
            ;
        root = addtree();
        count = 0;
        traversal(root, sumall, &count);
        if (contain(n, sumall, count))
            printf("yes\n");
        else
            printf("no\n");
        freet(root);
    }

    return 0;
}

/* addtree: add node to root */
Node *addtree(void)
{
    Node *tmp;
    int c, left, vcount;

    tmp = (Node *)malloc(sizeof(Node));
    tmp->left = tmp->right = NULL;
    left = FALSE;
    vcount = 0;
    while ((c = getchar()) != ')')
        /* left node */
        if (c == '(' && !left) {
            tmp->left = addtree();
            left = TRUE;
        }
        /* right node */
        else if (c == '(' && left)
            tmp->right = addtree();
        /* integer */
        else if (isdigit(c) || c == '-') {
            tmp->value[vcount] = c;
            tmp->value[++vcount] = '\0';
        }
    if (vcount == 0) {
        free(tmp);
        return NULL;
    }
    return tmp;
}

/* traversal: traversal the tree */
void traversal(Node *pt, int *sumall, int *count)
{
    int i, sum;
    /* empty tree */
    if (pt == NULL)
        return;
    /* leaf node: left and right are NULL */
    if (pt->left == pt->right) {
        for (sum = i = 0; i < stc; i++)
            sum += stack[i];
        sumall[(*count)++] = sum + atoi(pt->value);
        return;
    }
    /* push */
    stack[stc++] = atoi(pt->value);
    traversal(pt->left, sumall, count);
    traversal(pt->right, sumall, count);
    /* pop */
    stc--;
}

/* contain: if n contain in sumall,
 * return TRUE, else return FALSE */
int contain(int n, int *sumall, int count)
{
    int i;
    /* negative */
    if (count == 0 && n < 0)
        return TRUE;

    for (i = 0; i < count; i++)
        if (sumall[i] == n)
            return TRUE;
    return FALSE;
}

/* freet: free all node */
void freet(Node *pt)
{
    if (pt == NULL)
        return;
    freet(pt->left);
    freet(pt->right);
    free(pt);
}

ACM 386 Perfect Cubes

由題目給的答案可以發現:
n^3 = a^3 + b^3 + c^3,
a <= b <= c。
所以迴圈就這樣寫即可!
a從2開始,b從a開始,c從b開始。
當a^3 + b^3 + c^3 < n^3時,c + 1。
a^3 + b^3 + c^3 == n^3時,找到一組,
a^3 + b^3 + c^3 > n^3時,b + 1。
a^3 + b^3 > n^3時,a + 1。
a == n時,終止。

/* ACM 386 Perfect Cubes
 * mythnc
 * 2011/12/18 12:17:31   
 * run time: 0.132
 */
#include <stdio.h>

#define CUBIC(X) (X * X * X)

void cal(int);

int main(void)
{
    int i;

    for (i = 6; i <= 200; i++)
        cal(i);

    return 0;
}

/* cal: calculate answer */
void cal(int n)
{
    int i, j, k, value;

    for (i = 2; i < n; i++)
        for (j = i; CUBIC(i) + CUBIC(j) < CUBIC(n); j++)
            for (k = j; ; k++) {
                value = CUBIC(i) + CUBIC(j) + CUBIC(k);
                if (value < CUBIC(n))
                    continue;
                else if (value == CUBIC(n))
                    printf("Cube = %d, Triple = (%d,%d,%d)\n", n, i, j, k);
                else
                    break;
            }
}

20111217

ACM 10323 Factorial! You Must be Kidding!!!

如同題目所寫,
Factorial! You Must be Kidding!!!
沒啥規則的一題 = =,
input有可能是負數,所以要考慮負數的情況。
什麼?你說負數有階層?娜舞摳琳!
是的題目也這樣寫啊!
反正-1!是Overflow,
-2!是Underflow,
-3!是Overflow,
-4!是Underflow,
以此類推,
至於為什麼……不要問比較好,
問了會腦殘。
(-1! = 0! / 0,把他當成無限大去想,
-2! = -1! / -1,負無限大)

/* ACM 10323 Factorial! You Must be Kidding!!! 
 * mythnc
 * 2011/12/17 09:24:42   
 * run time: 0.028
 */
#include <stdio.h>

#define MAX 6227020800LL

int main(void)
{
    int i, n;
    long long ans[20] = { 1 };

    for (i = 1; ans[i - 1] < MAX; i++)
        ans[i] = ans[i - 1] * i;

    while (scanf("%d", &n) == 1)
        if (n < 0) {
            n *= -1;
            if (n % 2 == 1)
                printf("Overflow!\n");
            else
                printf("Underflow!\n");
        }
        else if (n < 8)
            printf("Underflow!\n");
        else if (n >= i)
            printf("Overflow!\n");
        else
            printf("%lld\n", ans[n]);

    return 0;
}

20111216

ACM 10010 Where's Waldorf?

填字遊戲。
大小寫視為相同。
找到字首後,從其四面八方找第二個字母,
找到第二個字母後,繼續從該路線找第三個字母,
一直到最後一個字母,若都有找到,就是找到了,
要不然就從下一個路線開始,
路線都沒找到,就從下一個點開始。
要考慮邊界問題。

很懶的用了一堆全域變數。

/* ACM 10010 Where's Waldorf? 
 * mythnc
 * 2011/12/16 12:31:16   
 * run time: 0.008
 */
#include <stdio.h>

#define MAXF  50
#define MAXK  20
#define DIRC  8
#define RANGE(X, Y) X >= 0 && X < maxrow && Y >= 0 && Y < maxcol
#define TRUE  1
#define FALSE 0

void findword(void);
int move(int, int, int);
int sameletter(char, char);

char square[MAXF][MAXF + 1];
char word[MAXK][MAXF + 1];
int maxrow, maxcol, kword;

int main(void)
{
    int n, i, j, set;

    scanf("%d", &n);
    for (i = set = 0; i < n; i++) {
        /* input data */
        scanf("%d %d", &maxrow, &maxcol);
        getchar(); /* eat newline */
        for (j = 0; j < maxrow; j++)
            scanf("%s", square[j]);
        scanf("%d", &kword);
        getchar(); /* eat newline */
        for (j = 0; j < kword; j++)
            scanf("%s", word[j]);
        /* blank line in each set */
        if (set == 1)
            putchar('\n');
        findword();
        set = 1;
    }

    return 0;
}

/* findword: find the word position */
void findword(void)
{
    int i, j, k, find;

    for (i = 0; i < kword; i++) {
        for (find = j = 0; j < maxrow && !find; j++)
            for (k = 0; k < maxcol && !find; k++)
                if (sameletter(word[i][0], square[j][k]))
                    find = move(i, j, k);
        if (find)
            printf("%d %d\n", j, k);
    }
}

/* move: move around from (r, c),
 * if find return TRUE, else return FALSE */
int move(int n, int r, int c)
{
    int mover[] = {-1, -1, -1, 0, 1, 1, 1, 0};
    int movec[] = {-1, 0, 1, 1, 1, 0, -1, -1};
    int i, j, nextr, nextc;

    for (i = 0; i < DIRC; i++) {
        nextr = r + mover[i];
        nextc = c + movec[i];
        j = 1;
        /* find word from direction i */
        while (word[n][j] != '\0' && RANGE(nextr, nextc)
               && sameletter(word[n][j], square[nextr][nextc])) {
            j++;
            nextr += mover[i];
            nextc += movec[i];
        }
        /* find */
        if (word[n][j] == '\0')
            return TRUE;
    }

    return FALSE;
}

/* sameletter: if the letter is same
 * (regardless of case)
 * return TRUE
 * else return FALSE */
int sameletter(char s, char t)
{
    if (s == t)
        return TRUE;
    /* upper to lower */
    if (s >= 'A' && s <= 'Z')
        s = s - 'A' + 'a';
    if (t >= 'A' && t <= 'Z')
        t = t - 'A' + 'a';

    if (s == t)
        return TRUE;
    return FALSE;
}

20111215

ACM 357 Let Me Count The Ways

ACM674
要用long long,要不然會爆掉。

/* ACM 357 Let Me Count The Ways
 * mythnc
 * 2011/12/15 11:15:23   
 * run time: 0.044
 */
#include <stdio.h>

#define MAXN 30001
#define COIN 5

void cal(long long *);

int main(void)
{
    int n;
    long long ans[MAXN] = { 0 };

    cal(ans);

    while (scanf("%d", &n) == 1)
        if (ans[n] == 1)
            printf("There is only 1 way to produce %d cents change.\n", n);
        else
            printf("There are %lld ways to produce %d cents change.\n", ans[n], n);

    return 0;
}

/* cal: pre calulate the answer */
void cal(long long *ans)
{
    int money[COIN] = {1, 5, 10, 25, 50};
    int i, j;

    ans[0] = 1;
    for (i = 0; i < COIN; i++)
        for (j = money[i]; j < MAXN; j++)
            ans[j] += ans[j - money[i]];
}

20111214

ACM 573 The Snail

蝸牛爬牆模擬題。
白天爬完要判斷一次,
晚上落後也要判斷一次。
當蝸牛不再爬後一直減到負數即可。

/* ACM 573 The Snail
 * mythnc
 * 2011/12/14 13:48:31   
 * run time: 0.020
 */
#include <stdio.h>

void snail(int, int, double, double);

int main(void)
{
    double fatigue, up;
    int wall, down;

    while (scanf("%d %lf %d %lf", &wall, &up, &down, &fatigue)
           && wall != 0) {
           snail(wall, down, up, fatigue);
    }

    return 0;
}

void snail(int wall, int down, double up, double fa)
{
    int day;
    double climb;
    /* init */
    day = 1;
    climb = 0.0;
    fa = up * fa / 100;
    while (1) {
        /* day climb */
        climb += up;
        if (climb > wall) {
            printf("success on day %d\n", day);
            return;
        }
        /* night fall */
        climb -= down;
        if (climb < 0.0) {
            printf("failure on day %d\n", day);
            return;
        }
        up -= fa;
        /* if up come to non-positive */
        if (up <= 0.0) {
            while (climb >= 0.0) {
                climb -= down;
                day++;
            }
            printf("failure on day %d\n", day);
            return;
        }
        /* another day */
        day++;
    }
}

20111213

ACM 147 Dollars

ACM674
先把面額都變成整數會比較好算,
1 / 0.05 = 20,
面額跟input都乘以20就能變整數。

/* ACM 147 Dollars
 * mythnc
 * 2011/12/13 14:56:54   
 * run time: 0.012
 */
#include <stdio.h>

#define MAXN 6001
#define COIN 11

void cal(long long *);

int main(void)
{
    long long ans[MAXN] = { 0 };
    double n;

    cal(ans);

    while (scanf("%lf", &n) && n != 0.0)
        printf("%6.2f%17lld\n", n, ans[(int)(n * 20)]);

    return 0;
}

/* cal: pre calulate answer */
void cal(long long *ans)
{
    int money[] = {1, 2, 4, 10, 20, 40,
                   100, 200, 400, 1000, 2000};
    int i, j;

    ans[0] = 1;
    for (i = 0; i < COIN; i++)
        for (j = money[i]; j < MAXN; j++)
            ans[j] += ans[j - money[i]];
}

ACM 674 Coin Change

經典DP題。
想了很久,還是想不出個所以然。
雖然看到公式也看到算法,還是不知道為什麼會這樣。
只能說……神奇啊
主要參考兩個網站:
algorithmistsagit教學
公式是長這樣:
c(i,j) = c(i - 1,j) + c(i,j - ai),
翻成國語是,
有i個元素要加總為j,共有c(i, j)的組合方式,
那麼,c(i, j)會等於i - 1個元素加總為j的組合,再加上i個元素加總為j - ai的組合。
很抽象吧……
舉個例子,
如果有硬幣面額1, 5, 10。
要算出0~26元的所有可能組合方式,
那就從面額1開始填0元到26元的可能,
0 1
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
9 1
10 1
11 1
12 1
13 1
14 1
15 1
16 1
17 1
18 1
19 1
20 1
21 1
22 1
23 1
24 1
25 1
26 1
接著填5,
0 1
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 2
9 2
10 3
11 3
12 3
13 3
14 3
15 4
16 4
17 4
18 4
19 4
20 5
21 5
22 5
23 5
24 5
25 6
26 6
看出來了嗎?
當只有1元時,所有可能的組合都是1種,
加入5元時,5元到9元的組合變成2(1 + 1)種,
10~14元的組合變成3(1 + 2)種,
對照公式c(i,n) = c(i - 1,n) + c(i,n - ai),
1元跟5元對n元的組合情況為,
1元對n的組合情況 + 1元跟5元對n - 5元的組合情況!
至於為什麼會這樣呢,看了幾個subset sum的證明,
發現看不是很懂(很數學),所以就先背起來吧,
反正這是正確的……

反正就是每當加入了一個新的面額X元,
要計算n元的組合情況,
就要從之前i - 1個面額對n元的組合情況,加上現在i個面額對n - x元的組合情況。

而起始要從0元開始,0元只有一種情況,
就是什麼都不拿。
1元會從1元開始 + 1,
5元會從5元開始 + 1,
依此類推。

所以若要計算n元,
先把陣列初始為0,陣列[0]設為1,
從面額最小的開始到面額最大的,
依序從該面額開始到n元填入所有的組合情況,
答案就出來了。

重點是從該面額開始到面額n,
然後0元也要考慮,(這樣n -x元若為0才有解)
這裡卡真久,
解釋的不是很好。
sagit的解說滿詳細的,可以看他的。

/* ACM 674 Coin Change
 * mythnc
 * 2011/12/13 13:48:59   
 * run time: 0.036
 */
#include <stdio.h>

#define MAXC 7490
#define COIN 5

void cal(int *);

int main(void)
{
    int ans[MAXC] = { 0 };
    int n;

    cal(ans);

    while (scanf("%d", &n) == 1)
        printf("%d\n", ans[n]);

    return 0;
}

/* cal: pre calculate answer */
void cal(int *ans)
{
    int i, j;
    int money[] = {1, 5, 10, 25, 50};

    ans[0] = 1;
    for (i = 0; i < COIN; i++)
        for (j = money[i]; j < MAXC; j++)
            ans[j] += ans[j - money[i]];
}

20111211

ACM 443 Humble Numbers

ACM136進階版。
不能直接output了,囧。

第一次嘗試用DP解題目,似乎也沒這麼難,
不過要想得到算法。
主要的想法參考於這裡
真是好厲害!

humble number = (2 ^ w) * (3 ^ x) * (5 ^ y) * (7 ^ z)
用DP的想法是,一開始只有1,1會變成2 3 5 7,
而2 3 5 7各自再乘上2 3 5 7後共有16種組合,
但是沒這麼多,因為有重複的情況。
前項的結果再乘上2 3 5 7,必定也是humble number,
(偉哉DP,好強大)
再繼續深入思考,
若一開始只有1,乘上2 3 5 7之後,取其中最小的一個數值。
這個情況下是2,這樣的話,下次質數2就是從第二個humble number開始乘(即2 * 2),
3 5 7還是要乘第一個質數(即3 * 1 5 * 1 7 * 1),
接著四個數值繼續做比大小的動作(4 3 5 7相比),
這個情況下是3,所以下次質數3要從第二個humble number開始乘(即3 * 2)
也就是說,每次取完最小的值,
下次該質數就要從下一個humble number做相乘的動作,
因為上一個humble number已經乘完啦!
但還沒結束,還要考慮humble number相同的情況,
例如3 * 5或5 * 3,是一樣的,
所以如果一樣,就必須把3跟5的紀錄次數各 + 1,
意即,每次找到最小值之後,只要是2 3 5 7的乘積與此值相等,
就各要 + 1。

最後要注意ordinal number,以前英文沒學好,-_-,傻傻搞不清楚。
只要十位數是1,都是加th,(不管個位數是不是1 2 3)
其他的情況下,1 2 3分別加st nd rd,
其餘都是th。

/* ACM 443 Humble Numbers
 * mythnc
 * 2011/12/11 21:49:31   
 * run time: 0.012
 */
#include <stdio.h>
#include <string.h>

#define MAXN 5842

void precal(int *);
void output(int, int *);

int main(void)
{
    int ans[MAXN];
    int n;

    precal(ans);

    while (scanf("%d", &n) && n != 0)
        output(n, ans);

    return 0;
}

/* precal: previous calculation the answer */
void precal(int *ans)
{
    int t2, t3, t5, t7;
    int p2, p3, p5, p7, count;

    p2 = p3 = p5 = p7 = 0;
    ans[0] = count = 1;
    while (count < MAXN) {
        t2 = ans[p2] * 2;
        t3 = ans[p3] * 3;
        t5 = ans[p5] * 5;
        t7 = ans[p7] * 7;
        /* find the minimal */
        if (t2 <= t3 && t2 <= t5 && t2 <= t7)
            ans[count] = t2;
        else if (t3 < t2 && t3 <= t5 && t3 <= t7)
            ans[count] = t3;
        else if (t5 < t2 && t5 < t3 && t5 <= t7)
            ans[count] = t5;
        else if (t7 < t2 && t7 < t3 && t7 < t5)
            ans[count] = t7;
        /* if same, prime number have to move to next */
        if (t2 == ans[count])
            p2++;
        if (t3 == ans[count])
            p3++;
        if (t5 == ans[count])
            p5++;
        if (t7 == ans[count])
            p7++;

        count++;
    }
}

/* output: print out the answer in the format */
void output(int n, int *ans)
{
    char number[3];

    if (n % 100 < 10 || n % 100 > 20)
        switch (n % 10) {
            case 1:
                strcpy(number, "st");
                break;
            case 2:
                strcpy(number, "nd");
                break;
            case 3:
                strcpy(number, "rd");
                break;
            default:
                strcpy(number, "th");
        }
    else
        strcpy(number, "th");

    printf("The %d%s humble number is %d.\n", n, number, ans[n - 1]);
}

20111210

ACM 640 Self Numbers

反過來做,找到所有的generator,
剩下的就不是generator。

用一個array做mark的動作。

/* ACM 640 Self Numbers
 * mythnc
 * 2011/12/10 20:41:52   
 * run time: 0.08
 */
#include <stdio.h>

#define MAXN  1000001
#define TRUE  1
#define FALSE 0

int sumd(int);

int mark[MAXN];

int main(void)
{
    int i, sum;

    for (i = 1; i < MAXN; i++)
        if (!mark[i]) {
            sum = sumd(i);
            while (sum < MAXN && !mark[sum]) {
                mark[sum] = TRUE;
                sum = sumd(sum);
            }
        }
    /* output */
    for (i = 1; i < MAXN; i++)
        if (!mark[i])
            printf("%d\n", i);

    return 0;
}

/* sumd: do d(n) and return the value */
int sumd(int n)
{
    int sum;

    for (sum = n; n; n /= 10)
        sum += n % 10;

    return sum;
}

20111209

ACM 10252 Common Permutation

說是排列組合,其實是要找兩個string中一樣的letter。

加總各個string的letter出現次數,
接著從a to z,若次數不為0,以最小的為主,
輸出字母次數。
直接看code比較清楚 @@

另外,可能會有空白列,所以用fgets()。
(scanf()就WA啦)

/* ACM 10252 Common Permutation
 * mythnc
 * 2011/12/09 08:57:30   
 * run time: 0.012
 */
#include <stdio.h>
#include <string.h>

#define MAXCHAR  1002
#define ALPHABET 26

void countl(char *, int *);
void output(int *, int *);

int main(void)
{
    char line[MAXCHAR];
    int p1[ALPHABET], p2[ALPHABET];

    while (fgets(line, MAXCHAR, stdin)) {
        memset(p1, 0, sizeof(p1));
        countl(line, p1);
        fgets(line, MAXCHAR, stdin);
        memset(p2, 0, sizeof(p2));
        countl(line, p2);
        output(p1, p2);
    }

    return 0;
}

/* countl: count letter numbers */
void countl(char *line, int *p)
{
    int i;

    for (i = 0; i < strlen(line) - 1; i++)
        if (line[i] >= 'a' && line[i] <= 'z')
            p[line[i] - 'a']++;
}

/* output: output result in alphabetical order */
void output(int *p1, int *p2)
{
    int i;

    for (i = 0; i < ALPHABET; i++)
        for (; p1[i] > 0 && p2[i] > 0; p1[i]--, p2[i]--)
                printf("%c", 'a' + i);
    putchar('\n');
}

20111208

ACM 10220 I Love Big Numbers !

大數運算乘法,之後做位數加總。

/* ACM 10220 I Love Big Numbers !
 * mythnc
 * 2011/12/08 09:38:23   
 * run time: 0.020
 */
#include <stdio.h>

#define MAXD 2570
#define MAXN 1000

int bigmul(int *, int, int);
int sumdigit(int *, int);

int main(void)
{
    int n, i, digit;
    int answer[MAXN];
    int big[MAXD] = { 1 };
    /* pre calculate */
    for (i = digit = 1; i <= 1000; i++) {
        digit = bigmul(big, i, digit);
        answer[i - 1] = sumdigit(big, digit);
    }

    while (scanf("%d", &n) == 1)
        printf("%d\n", answer[n - 1]);

    return 0;
}

/* bigmul: do big * n, return its digit */
int bigmul(int *big, int n, int digit)
{
    int i;

    for (i = 0; i < digit; i++)
        big[i] *= n;
    /* find new digit */
    if (n < 10)
        digit++;
    else if (n < 100)
        digit += 2;
    else if (n < 1000)
        digit += 3;
    else
        digit += 4;
    /* carry */
    for (i = 0; i < digit; i++)
        if (big[i] > 9) {
            big[i + 1] += big[i] / 10;
            big[i] %= 10;
        }

    while (big[digit] == 0)
        digit--;
    return digit + 1;
}

/* sumdigit: return the sum of digits */
int sumdigit(int *big, int n)
{
    int i, sum;

    for (i = sum = 0; i < n; i++)
        sum += big[i];

    return sum;
}

20111207

ACM 440 Eeny Meeny Moo

ACM151

/* ACM 440 Eeny Meeny Moo
 * mythnc
 * 2011/12/07 08:07:57   
 * run time: 0.068
 */
#include <stdio.h>

#define MAXN 149

typedef struct list {
    int i;
    struct list *next;
    struct list *pre;
} List;

void init(List *);
int findm(List *, int);
void link(List *, int);

int main(void)
{
    List array[MAXN];
    int n, i;
    int ans[MAXN];

    init(array);

    for (i = 3; i < MAXN + 1; i++)
        ans[i - 1] = findm(array, i);

    while (scanf("%d", &n) && n != 0)
        printf("%d\n", ans[n - 1]);

    return 0;
}

/* init: initialize array */
void init(List *array)
{
    int i;

    for (i = 0; i < MAXN; i++)
        array[i].i = i + 1;
}

/* findm: find and return the number m */
int findm(List *ary, int n)
{
    int count, i, m;
    List *p;

    for (m = 2; ; m++) {  /* find m */
        link(ary, n);     /* link all again */
        p = ary;
        count = 1;
        while (count < n) {
            p->pre->next = p->next;  /* link arrange */
            p->next->pre = p->pre;
            for (i = 0; i < m; i++)  /* move */
                p = p->next;
            count++;
            if (p->i == 2)
                break;
        }
        if (count == n)
            return m;
    }
}

/* link: link array elements one by one
 * in a circle */
void link(List *p, int n)
{
    int i;

    p[0].next = &p[1];
    p[0].pre = &p[n - 1];
    for (i = 1; i < n; i++) {
        p[i].next = &p[(i + 1) % n];
        p[i].pre = &p[i - 1];
    }
}

20111206

ACM 834 Continued Fractions

做輾轉相除法!

/* ACM 834 Continued Fractions
 * mythnc
 * 2011/12/06 12:05:34   
 * run time: 0.012
 */
#include <stdio.h>

#define MAXK 1000

int cal(int, int, int *, int);
void print(int *, int);

int main(void)
{
    int a, b, count;
    int answer[MAXK];

    while (scanf("%d %d", &a, &b) == 2) {
        count = 0;
        if (a < b)
            answer[count++] = 0;
        count = cal(a, b, answer, count);
        print(answer, count);
    }

    return 0;
}

/* cal: find n and save it in ans
 * return the numbers */
int cal(int a, int b, int *ans, int count)
{
    while (a != 0 && b != 0)
        if (a > b) {
            ans[count++] = a / b;
            a %= b;
        }
        else {
            ans[count++] = b / a;
            b %= a;
        }

    return count;
}

/* print: print out result */
void print(int *ans, int n)
{
    int i;

    if (n == 1) {
        printf("[%d]\n", ans[0]);
        return;
    }
    printf("[%d;%d", ans[0], ans[1]);
    for (i = 2; i < n; i++) {
        printf(",%d", ans[i]);
    }
    printf("]\n");
}

20111205

ACM 118 Mutant Flatworld Explorers

模擬題。
我用0~3代表NESW。
遇到R就+1,L就-1。
活路就直接移動
死路的話判斷有無mark過。
有mark,不移動;
沒mark,output。

邊界問題WA一次 orz。

/* ACM 118 Mutant Flatworld Explorers
 * mythnc
 * 2011/12/05 14:23:59   
 * run time: 0.008
 */
#include <stdio.h>

#define MAXCO   51  /* 0 to 50 */
#define MAXCHAR 101
#define TRUE    1
#define FALSE   0
#define RANGE(X, Y) X >= 0 && X <= Y

typedef struct robot {
    int row, col, dir;
} Robot;

int ctoi(char);
int takeorder(Robot *, char *, int (*)[]);
int changedir(int);
void output(Robot);

int row, col;

int main(void)
{
    int fall;
    int mark[MAXCO][MAXCO] = { FALSE };
    char dir;
    char order[MAXCHAR];
    Robot ro;

    scanf("%d %d", &col, &row);
    while (scanf("%d %d %c", &ro.col, &ro.row, &dir) == 3) {
        getchar(); /* eat newline */
        scanf("%s", order);
        getchar(); /* eat newline */
        ro.dir = ctoi(dir);
        fall = takeorder(&ro, order, mark);
        output(ro);
        if (fall)
            printf(" LOST");
        putchar('\n');
    }

    return 0;
}

/* coti: convert c to correspond int
 * and return it */
int ctoi(char c)
{
    switch (c) { /* NESW <=> 0123 */
        case 'N':
            return 0;
        case 'E':
            return 1;
        case 'S':
            return 2;
        case 'W':
            return 3;
    }
}

/* takeorder: take the order,
 * do the correspond action,
 * and return fall or not */
int takeorder(Robot *ro, char *order, int (*mark)[MAXCO])
{
    int i, fall, nextr, nextc;
    int movec[] = {0, 1, 0, -1};
    int mover[] = {1, 0, -1, 0};

    for (fall = FALSE, i = 0; order[i] != '\0'; i++) {
        switch (order[i]) {
            case 'R':
                ro->dir = changedir(ro->dir + 1);
                break;
            case 'L':
                ro->dir = changedir(ro->dir - 1);
                break;
            case 'F':
                nextr = ro->row + mover[ro->dir];
                nextc = ro->col + movec[ro->dir];
                if (RANGE(nextc, col) && RANGE(nextr, row)) {
                    ro->col = nextc;
                    ro->row = nextr;
                }
                else if (!mark[ro->row][ro->col]){
                    mark[ro->row][ro->col] = TRUE;
                    fall = TRUE;
                }
        }
        if (fall)
            return fall;
    }

    return fall;
}

/* changedir: change the direction of ro */
int changedir(int dir)
{
    if (dir > 3)
        return dir % 4;
    else if (dir < 0)
        return dir + 4;
    return dir;
}

/* output: the information of robot */
void output(Robot ro)
{
    char s[] = "NESW";

    printf("%d %d %c", ro.col, ro.row, s[ro.dir]);
}

ACM 10970 Big Chocolate

奇怪的題目,應該是出錯吧?
2 2最少刀是2刀。
不過照他的測資給他答案是3刀。
這樣也沒什麼好算的,直接output答案。

/* ACM 10970 Big Chocolate
 * mythnc
 * 2011/12/05 13:32:12   
 * run time: 0.040
 */
#include <stdio.h>

int main(void)
{
    int m, n;

    while (scanf("%d %d", &m, &n) == 2)
        printf("%d\n", m * n - 1);

    return 0;
}

20111203

ACM 10424 Love Calculator

先對字串做mapping,之後可以得到number,
若number >= 10,需要再做mapping。
直到number < 10為止。

因為要算比例,不能超過100%。
所以會是a / b或b / a其中一種。
端看b > a或a > b。

/* ACM 10424 Love Calculator
 * mythnc
 * 2011/12/03 09:29:34   
 * run time: 0.004
 */
#include <stdio.h>

#define MAXCHAR 27 /* 25 + '\n' + '\0' */

double value(char *);
int onedigit(int);

int main(void)
{
    char name[2][MAXCHAR];
    double v[2];
    int i;

    while (fgets(name[0], MAXCHAR, stdin)) {
        fgets(name[1], MAXCHAR, stdin);
        for (i = 0; i < 2; i++)
            v[i] = value(name[i]);
        if (v[0] >= v[1])
            printf("%.2f %%\n", 100 * v[1] / v[0]);
        else
            printf("%.2f %%\n", 100 * v[0] / v[1]);
    }

    return 0;
}

/* value: return the value of name */
double value(char *s)
{
    int i, sum;

    for (i = sum = 0; s[i] != '\n'; i++)
        if (s[i] >= 'a' && s[i] <= 'z')
            sum += s[i] - 'a' + 1;
        else if (s[i] >= 'A' && s[i] <= 'Z')
            sum += s[i] - 'A' + 1;

    while (sum > 9)
        sum = onedigit(sum);

    return (double)sum;
}

/* onedigit: make sure n is one digit */
int onedigit(int n)
{
    int sum;

    for (sum = 0; n != 0;) {
        sum += n % 10;
        n /= 10;
    }

    return sum;
}

20111202

ACM 713 Adding Reversed Numbers

題目寫得烙烙長其實只是做大數運算加法。
先扣掉尾端的0,之後就能直接做加法。
做完加法之後,最前端的0也不要輸出即可。

練習寫虛擬碼看看……
eat token1 token2
cutouttrailingzero(token1)
cutouttrailingzero(token2)
sum = sumbig(token1 token2)
cutoutleadingzero(sum)
print(sum)

/* ACM 713 Adding Reversed Numbers
 * mythnc
 * 2011/12/02 12:34:53   
 * run time: 0.008
 */
#include <stdio.h>
#include <string.h>

#define MAXD 201

void ignorezero(char *, int);
int bigsum(char (*)[], int *);
void print(int *, int);

int main(void)
{
    char v[2][MAXD];
    int sum[MAXD];
    int i, digit;

    scanf("%*d");
    while (scanf("%s %s", v[0], v[1]) == 2) {
        for (i = 0; i < 2; i++)
            ignorezero(v[i], strlen(v[i]) - 1);
        memset(sum, 0, sizeof(sum));
        digit = bigsum(v, sum);
        print(sum, digit);
    }

    return 0;
}

/* ignorezero: cutting of trailing zero */
void ignorezero(char *s, int i)
{
    while (s[i] == '0')
        i--;

    s[i + 1] = '\0';
}

/* bigsum: do v[0] + v[1] = sum,
 * return sum's digit */
int bigsum(char (*v)[MAXD], int *sum)
{
    int i, j;

    for (i = 0; i < strlen(v[0]) && i < strlen(v[1]); i++)
        sum[i] += v[0][i] - '0' + v[1][i] - '0';
    for (; i < strlen(v[0]); i++)
        sum[i] += v[0][i] - '0';
    for (; i < strlen(v[1]); i++)
        sum[i] += v[1][i] - '0';
    /* carry */
    for (j = 0; j < i; j++)
        if (sum[j] > 9) {
            sum[j + 1] += sum[j] / 10;
            sum[j] %= 10;
        }

    if (sum[j] != 0)
        return j + 1;
    return j;
}

/* print: print out result */
void print(int *sum, int n)
{
    int i;
    /* do not print out leading zeros */
    for (i = 0; sum[i] == 0; i++)
        ;
    for (; i < n; i++)
        printf("%d", sum[i]);
    putchar('\n');
}

20111201

ACM 305 Joseph

「before the first good guy」,
有兩個意思:
(a)排在好人列裡第一個好人,
(b)所有好人中的第一個好人。
本題採用(b),翻成國語就是
所有的好人都不能被殺死啦!

ACM 151的方法去解,
但是不管演算法再怎麼修,
從k = 1算到k = 13都要花10秒的時間。
最後只好先算好答案再上傳了,囧。

後來參考別人的算法,
發現一招很厲害可以直接算的算則。

因為好人一定是前k個,壞人一定是最後的k個。
(共2k個)。
所以每次數完人頭後,m一定要是第k + 1個之後的數值,
否則就會殺到好人。
以k = 3為例,m直接用5代入。
0 1 2 3 4 5
g g g b b b
從0開始 + 5 - 1,為4,
4不為好人,之後刪掉一個壞人繼續做運算。
0 1 2 3 4
g g g b b
4 + (5 - 1) = 8
8 % 5(現有人口) = 3
3也不為好人,再刪掉一個壞人繼續做運算。
0 1 2 3
g g g b
3 + (5 - 1) = 7
7 % 4(現有人口) = 3
3也不為好人,再刪掉一個壞人。
現有人口為3,即k值。
表示當人口為k值時,即可停止運算,會傳m值。
當loop結束,而人口不為k值,表示殺錯人了,
m + 1從頭繼續運算。
真是好威的方法!

TLE code就不貼了。
用linked list移來移去好蠢,囧。

/* ACM 305 Joseph
 * mythnc
 * 2011/12/01 14:33:02   
 * version 2
 * run time: 0.076
 */
#include <stdio.h>

#define MAX 13

int findm(int);

int main(void)
{
    int k, i;
    int answer[MAX];

    for (i = 0; i < MAX; i++)
        answer[i] = findm(i + 1);

    while (scanf("%d", &k) && k != 0)
        printf("%d\n", answer[k - 1]);

    return 0;
}

/* findm: return minimum m */
int findm(int k)
{
    int m, re, position;

    for (m = k + 1; ; m++) {
        for (position = 0, re = 2 * k; re > k; re--) {
            position = (position + m - 1) % re;
            if (position < k)
                break;
        }
        if (re == k)
            return m;
    }
}


從1開始的版本寫完發現還是從0開始好 = =。
int findm(int k)
{
    int m, re, position;

    for (m = k + 1; ; m++) {
        for (re = 2 * k, position = 0; re > k; re--) {
            if (position == 0)
                position = m % re;
            else
                position = (position + m - 1) % re;
            if (position <= k && position != 0)
                break;
        }
        if (re == k)
            return m;
    }
}

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

ACM 10137 The Trip

討厭的浮點數運算,真是會整死人。
要做的事有兩個:
(1)四捨五入到cent
(2)找出最小的找零數

四捨五入下篇做詳解,反正就是那樣做。
找出最小的零頭,就是要作兩種版本的sum,
選擇其中之一較小的即可。

忘記寫return 0又吃了幾次RE = ="。

/* ACM 10137 The Trip
 * mythnc
 * 2011/11/27 20:38:34   
 * run time: 0.012
 */
#include <stdio.h>

#define MAXS 1000

double input(double *, int);
double roundoff(double);
double exchange(double *, double, int);

int main(void)
{
    double student[MAXS];
    double average;
    int n;

    while (scanf("%d", &n) && n != 0) {
        average = roundoff(input(student, n));
        printf("$%.2f\n", exchange(student, average, n));
    }

    return 0;
}

/* input: receive input data, return average cost */
double input(double *s, int n)
{
    int i;
    double sum;

    for (i = sum = 0; i < n; i++) {
        scanf("%lf", s + i);
        sum += s[i];
    }

    return sum / n;
}

/* roundoff: roundoff average to cent */
double roundoff(double money)
{
    return (int)(money * 100 + 0.5) / 100.0;
}

/* exchange: return total amount of exchange money */
double exchange(double *s, double average, int n)
{
    int i;
    double sum1, sum2;

    for (i = sum1 = sum2 = 0; i < n; i++) {
        if (s[i] > average)
            sum1 += s[i] - average;
        if (s[i] < average)
            sum2 += average - s[i];
    }

    return (sum1 < sum2) ? sum1 : sum2;
}

20111126

ACM 10161 Ant on a Chessboard

數值排列找規則。
若該數為x的平方數,那
一定是(1, x)或(x, 1),
看x是奇數或偶數。
若該數比平方數大,會有三種情況:
(1) 該數 - 平方數 == x + 1:
那就是在斜角(x + 1, x + 1)
(2) 該數 - 平方數 < x + 1:
(該數 - 平方數, n + 1)或(n + 1, 該數 - 平方數)
看x是奇數或偶數。
(3) 該數 - 平方數 > x + 1:
其中一個為n + 1,
另一個為n + 1 - [該數 - 平方數 - (n + 1)]
n + 1的意思是斜角。
算出與斜角的差值,再用此值與斜角相減。
(即從斜角的座標往前推)
化簡後為
2n + 2 - (該數 - 平方數)

/* ACM 10161 Ant on a Chessboard
 * mythnc
 * 2011/11/26 16:49:42   
 * run time: 0.004
 */
#include <stdio.h>
#include <math.h>

void output(int);

int main(void)
{
    int x;

    while (scanf("%d", &x) && x != 0)
        output(x);

    return 0;
}

/* output: output answer */
void output(int x)
{
    int n, value;

    if (x == 1) {
        printf("1 1\n");
        return;
    }

    n = sqrt(x);
    /* if x is a square number */
    if (n * n == x) {
        if (n % 2 == 1)
            printf("%d %d\n", 1, n);
        else
            printf("%d %d\n", n, 1);
        return;
    }
    /* if x bigger than a square number n */
    value = x - n * n;
    if (value == n + 1)
        printf("%d %d\n", n + 1, n + 1);
    else if (value < n + 1)
        if (n % 2 == 1)
            printf("%d %d\n", value, n + 1);
        else
            printf("%d %d\n", n + 1, value);
    else
        if (n % 2 == 1)
            printf("%d %d\n", n + 1, 2 * n - value + 2);
        else
            printf("%d %d\n", 2 * n - value + 2, n + 1);
}

20111125

ACM 414 Machined Surfaces

左邊跟右邊的XX合併,
合併後找出剩餘的space。

合併規則:左側最右與右側最左合併之。
那一定是左側 + 右側最多XX的那一個row,
會先併在一起。
也就是space最少的那個row。

所以找出space最小的row,
此row再跟其他row的space做相減加總後,
即為答案。

/* ACM 414 Machined Surfaces
 * mythnc
 * 2011/11/25 09:06:42   
 * run time: 0.004
 */
#include <stdio.h>

#define MAXCHAR 27 /* 25 + newline + '\0' */
#define MAXLINE 12

void input(char (*)[], int);
int count(char (*)[], int);

int main(void)
{
    char str[MAXLINE][MAXCHAR];
    int n, i;

    while (scanf("%d", &n) && n != 0) {
        getchar();   /* eat newline */
        input(str, n);
        printf("%d\n", count(str, n));
    }
    return 0;
}

/* input: receive input data */
void input(char (*s)[MAXCHAR], int n)
{
    int i;

    for (i = 0; i < n; i++)
        fgets(s[i], MAXCHAR, stdin);
}

/* count: return the count of remain space numbers */
int count(char (*s)[MAXCHAR], int n)
{
    int i, j, min, sum;
    int countb[MAXLINE];

    for (i = 0, min = 25; i < n; i++) {
        /* calculate space number in each row */
        for (j = countb[i] = 0; j < MAXCHAR; j++)
            if (s[i][j] == ' ')
                countb[i]++;
        /* find the minimum space number */
        if (min > countb[i])
            min = countb[i];
    }

    for (i = sum = 0; i < n; i++)
        sum += countb[i] - min;

    return sum;
}

20111124

ACM 412 Pi

排列組合 + 找出gcd為1的pair。
之後可求pi值。

/* ACM 412 Pi
 * mythnc
 * 2011/11/24 10:50:36   
 * run time: 0.124
 */
#include <stdio.h>
#include <math.h>

#define MAXN 49

void input(int *, int);
int comfactor(int *, int);
int gcd(int, int);

int main(void)
{
    int n, pair;
    int list[MAXN];

    while (scanf("%d", &n) == 1 && n != 0) {
        input(list, n);
        pair = comfactor(list, n);
        if (pair == 0) {
            printf("No estimate for this data set.\n");
            continue;
        }
        printf("%.6f\n", sqrt(3.0 / pair * n * (n - 1)));
    }
    return 0;
}

/* input: receive input data */
void input(int *list, int n)
{
    int i;

    for (i = 0; i < n; i++)
        scanf("%d", list + i);
}

/* comfactor: return no common factors pair numbers */
int comfactor(int *list, int n)
{
    int i, j, count;

    for (count = i = 0; i < n - 1; i++)
        for (j = i + 1; j < n; j++)
            if (gcd(list[i], list[j]) == 1)
                count++;

    return count;
}

/* gcd: calculate gcd of a and b */
int gcd(int a, int b)
{
    while ((a %= b) && (b %= a))
        ;

    return a + b;
}

20111123

ACM 10013 Super long sums

大數運算加法。
進位問題沒考慮好,WA一次。

9999 + 0001 = 0000。

/* ACM 10013 Super long sums
 * mythnc
 * 2011/11/23 14:17:30   
 * run time: 1.200
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXD 1000001

void bigsum(int *, int);
void print(int *, int);

int main(void)
{
    int set, i, m;
    int *sum;

    sum = (int *)malloc(sizeof(int) * MAXD);

    scanf("%d", &set);
    for (i = 0; i < set; i++) {
        /* init */
        memset(sum, 0, MAXD * sizeof(int));
        scanf("%d", &m);
        bigsum(sum, m);
        print(sum, m - 1);
        if (i < set - 1)
            putchar('\n');
    }

    free(sum);

    return 0;
}

/* bigsum: scan two number x, y, then do x + y */
void bigsum(int *sum, int m)
{
    int i, x, y;

    for (i = m - 1; i > - 1; i--) {
        scanf("%d %d", &x, &y);
        sum[i] += x + y;
    }
    /* carry */
    for (i = 0; i < m; i++)
        if (sum[i] > 9) {
            sum[i + 1] += sum[i] / 10;
            sum[i] %= 10;
        }
}

/* print: print out sum */
void print(int *sum, int m)
{
    for (; m > -1; m--)
        printf("%d", sum[m]);
    putchar('\n');
}

20111122

ACM 623 500!

一開始直接做超過3秒 TE。
只好事先算出答案,
結果忘記考慮0! ,
WA一次。

/* ACM 623 500!
 * mythnc
 * 2011/11/22 21:08:08   
 * run time: 0.084
 */
#include <stdio.h>

#define MAXD 2600
#define MAXN 1001

int mul(int *, int);
void copy(int *, char *, int);

int main(void)
{
    int n, i, digit;
    int big[MAXD] = { 1 };
    char fac[MAXN][MAXD];
    /* precalculate */
    for (i = 0; i < MAXN; i++) {
        digit = mul(big, i);
        copy(big, fac[i], digit);
    }

    while (scanf("%d", &n) == 1)
        printf("%d!\n%s", n, fac[n]);

    return 0;
}

/* mul: do b * n return its digit */
int mul(int *b, int n)
{
    int i;

    if (n == 0)
        return 0;

    for (i = 0; i < MAXD; i++)
        b[i] *= n;
    /* carry */
    for (i = 0; i < MAXD - 1; i++)
        if (b[i] > 9) {
            b[i + 1] += b[i] / 10;
            b[i] %= 10;
        }
    /* find digit */
    for (i = MAXD - 1; b[i] == 0 && i > -1; i--)
        ;

    return i;
}

/* copy: copy b to f */
void copy(int *b, char *f, int d)
{
    int i;

    for (i = 0; d > -1; i++, d--)
        f[i] = b[d] + '0';
    f[i++] = '\n';
    f[i] = '\0';
}

20111121

ACM 10921 Find the Telephone

map then output。

做了快100題了,
之前都是一天兩題。
之後每天做一題就好。
現在的題目難易落差明顯。
增強實力吧~!

/* ACM 10921 Find the Telephone
 * mythnc
 * 2011/11/21 12:12:59   
 * run time: 0.004
 */
#include <stdio.h>

#define MAXCHAR 31

void trans(char *);

int main(void)
{
    char s[MAXCHAR];

    while (scanf("%s", s) == 1) {
        trans(s);
        printf("%s\n", s);
    }
    return 0;
}

/* trans: translate s into the form of telephone number */
void trans(char *s)
{
    int i;
    char map[] = {'2', '2', '2', '3', '3', '3', '4', '4', '4', '5',
                  '5', '5', '6', '6', '6', '7', '7', '7', '7', '8',
                  '8', '8', '9', '9', '9', '9'};

    for (i = 0; s[i] != '\0'; i++)
        if (s[i] >= 'A' && s[i] <= 'Z')
            s[i] = map[s[i] - 'A'];
}

20111120

ACM 484 The Department of Redundancy Department

Linked list。
好久沒寫都快忘光惹 @_@。

/* ACM 484 The Department of Redundancy Department
 * mythnc
 * 2011/11/20 14:10:46   
 * run time: 0.480
 */
#include <stdio.h>
#include <stdlib.h>

typedef struct number {
    int name;
    int count;
    struct number *next;
} Number;

Number *addnumber(int, Number *);
void print(Number *);
void freenumber(Number *);

int main(void)
{
    Number *root;
    int n;

    root = NULL;
    while (scanf("%d", &n) == 1)
        root = addnumber(n, root);

    print(root);
    freenumber(root);

    return 0;
}

/* addnumber: add number in p */
Number *addnumber(int n, Number *p)
{
    if (p == NULL) {
        p = (Number *)malloc(sizeof(Number));
        p->name = n;
        p->count = 1;
        p->next = NULL;
    }
    else if (p->name == n)
        p->count++;
    else
        p->next = addnumber(n, p->next);

    return p;
}

/* print: print p */
void print(Number *p)
{
    while (p != NULL) {
        printf("%d %d\n", p->name, p->count);
        p = p->next;
    }
}

/* freenumber: free p */
void freenumber(Number *p)
{
    Number *tmp;

    while (p != NULL) {
        tmp = p;
        free(tmp);
        p = p->next;
    }
}

20111119

ACM 10929 You can say 11

判斷是否為11的倍數。

做(奇數位數和 - 偶數位數和) % 11即可。

神秘的數字11。

/* ACM 10929 You can say 11
 * mythnc
 * 2011/11/19 22:51:54   
 * run time: 0.016
 */
#include <stdio.h>

#define MAXCHAR 1001

int mul11(char *);

int main(void)
{
    char s[MAXCHAR];

    while (scanf("%s", s) == 1 && (s[0] != '0' || s[1] != '\0'))
        if (mul11(s))
            printf("%s is a multiple of 11.\n", s);
        else
            printf("%s is not a multiple of 11.\n", s);

    return 0;
}

/* mul11: return 1 if s can be expressed 11n
 * else return 0 */
int mul11(char *s)
{
    int i, odd, even;

    for (i = odd = even = 0; s[i] != '\0'; i++)
        if (i % 2 == 0)
            odd += s[i] - '0';
        else
            even += s[i] - '0';

    return (odd - even)% 11 == 0;
}

ACM 10260 Soundex

字轉成數字,
重複跟0不做輸出。

/* ACM 10260 Soundex
 * mythnc
 * 2011/11/19 21:42:21   
 * run time: 0.004
 */
#include <stdio.h>

#define MAXCHAR 20

void print(char *);

int main(void)
{
    char str[MAXCHAR];
    
    while (scanf("%s", str) == 1)
        print(str);
    return 0;
}

/* print: print out sounedex code */
void print(char *s)
{
    int map[26] = {0, 1, 2, 3, 0,
                   1, 2, 0, 0, 2,
                   2, 4, 5, 5, 0,
                   1, 2, 6, 2, 3,
                   0, 1, 0, 2, 0, 2};

    int stack, i;

    for (stack = -1, i = 0; s[i] != '\0'; i++)
        if (stack != map[s[i] - 'A'])
            if (map[s[i] - 'A'] != 0) {
                printf("%d", map[s[i] - 'A']);
                stack = map[s[i] - 'A'];
            }
            else
                stack = -1;
    putchar('\n');
}

20111118

ACM 10924 Prime Words

判斷質數。
ACM有些題目會把1定義為質數,
但1明明就不是質數,
真瞎,快改吧!

/* ACM 10924 Prime Words
 * mythnc
 * 2011/11/18 09:56:41   
 * run time: 0.004
 */
#include <stdio.h>
#include <string.h>
#include <math.h>

#define MAXCHAR 21
#define TRUE    1
#define FALSE   0

int sumw(char *);
int prime(int n);

int main(void)
{
    char str[MAXCHAR];

    while (scanf("%s", str) == 1) {
        if (prime(sumw(str)))
            printf("It is a prime word.\n");
        else
            printf("It is not a prime word.\n");
    }
    return 0;
}

/* sumw: sum of the word letters */
int sumw(char *s)
{
    int i, sum;

    for (i = sum = 0; i < strlen(s); i++)
        if (s[i] >= 'a' && s[i] <= 'z')
            sum += s[i] - 'a' + 1;
        else if (s[i] >= 'A' && s[i] <= 'Z')
            sum += s[i] - 'A' + 27;

    return sum;
}

/* prime: return TRUE if n is prime,
 * else return FALSE */
int prime(int n)
{
    int i;

    if (n == 2)
        return TRUE;

    if (n % 2 == 0)
        return FALSE;

    for (i = 3; i <= sqrt(n); i += 2)
        if (n % i == 0)
            return FALSE;

    return TRUE;
}

ACM 324 Factorial Frequencies

大數運算乘法。
再找0~9出現次數。

/* ACM 324 Factorial Frequencies
 * mythnc
 * 2011/11/18 09:13:32   
 * run time: 0.012
 */
#include <stdio.h>

#define MAXD 781

void init(int *, int);
int mul(int *, int);
void countdec(int *, int, int *);
void print(int, int *);

int main(void)
{
    int n, count;
    int seq[MAXD];
    int dec[10];

    while (scanf("%d", &n) && n != 0) {
        init(seq, MAXD);
        init(dec, 10);
        count = mul(seq, n);
        countdec(seq, count, dec);
        print(n, dec);
    }
    return 0;
}

/* init: initialized s to zero */
void init(int *s, int n)
{
    int i;

    for (i = 0; i < n; i++)
        s[i] = 0;
}

/* mul: calculate n!,
 * return its digit number */
int mul(int *s, int n)
{
    int i, j;

    for (s[0] = 1, i = 2; i < n + 1; i++) {
        for (j = 0; j < MAXD; j++)
            s[j] *= i;
            /* carry */
        for (j = 0; j < MAXD; j++)
            if (s[j] > 9) {
                s[j + 1] += s[j] / 10;
                s[j] %= 10;
            }
    }

    for (i = MAXD - 1; s[i] == 0; i--)
        ;

    return i + 1;
}

/* countdec: count 0~9 times */
void countdec(int *s, int n, int *d)
{
    int i;

    for (i = 0; i < n; i++)
        d[s[i]]++;
}

/* print: print out result */
void print(int n, int *d)
{
    int i;

    printf("%d! --\n", n);
    for (i = 0; i < 10; i++) {
        if (i == 5)
            printf("\n");
        if (i != 0 && i != 5)
            printf(" ");
        printf("   (%d)%5d", i, d[i]);
    }
    printf("\n");
}

ACM 583 Prime Factors

質因數分解。
沒用sqrt就TL,
用sqrt變超快!
看來我的i * i <= n以後都要寫,
i <= sqrt(n)了 。

/* ACM 583 Prime Factors
 * mythnc
 * 2011/11/17 22:56:12   
 * run time: 0.340
 */
#include <stdio.h>
#include <math.h>

void factor(int);
int print(int, int);

int main(void)
{
    int n;

    while (scanf("%d", &n) && n != 0) {
        printf("%d = ", n);
        if (n < 0) {
            printf("-1 x ");
            n *= -1;
        }
        factor(n);
    }
    return 0;
}

/* factor: find prime factor and print it */
void factor(int n)
{
    int i;
    /* even */
    while (n % 2 == 0 && n != 1)
        n = print(n, 2);
    if (n == 1)
        return;
    /* odd */
    for (i = 3; i <= sqrt(n) && n != 1; i += 2)
        while (n % i == 0 && n != 1)
            n = print(n, i);
    if (n == 1)
        return;
    /* prime */
    if (n != 1)
        printf("%d\n", n);
}

/* print: print prime and return n */
int print(int n, int i)
{
    n /= i;
    if (n == 1) {
        printf("%d\n", i);
        return 1;
    }
    else
        printf("%d x ", i);

    return n;
}

20111117

ACM 350 Pseudo-Random Numbers

search。
注意不一定從種子開始,
也就意味第一個數可能是非cycle數。

/* ACM 350 Pseudo-Random Numbers
 * mythnc
 * 2011/11/17 21:48:38   
 * run time: 1.1
 */
#include <stdio.h>

#define MAXR  10000

int len(int, int, int, int);
int in(int, int *, int);

int main(void)
{
    int z, inc, mod, last, set;

    set = 0;
    while (scanf("%d %d %d %d", &z, &inc, &mod, &last) && mod != 0)
        printf("Case %d: %d\n", ++set, len(z % mod, inc % mod, mod, last));

    return 0;
}

/* len: return the len */
int len(int z, int inc, int mod, int last)
{
    int i, tmp, seed;
    int r[MAXR];

    r[0] = last;
    for (i = 1; ; i++) {
        tmp = (r[i - 1] * z + inc) % mod;
        if ((seed = in(tmp, r, i)) != -1)
            return i - seed;
        r[i] = tmp;
    }
}

/* in: if n in r,
 * i = 0 mean start from seed return TRUE,
 * i > 0 mean start not from seed return i,
 * else return -1 */
int in(int n, int *r, int count)
{
    int i;

    for (i = 0; i < count; i++)
        if (r[i] == n)
            return i;

    return -1;
}

ACM 294 Divisors

找因數。
ACM 160類似。

先做質因數分解,再用質因數分解的答案算出所有因數。
例如6是 2^1 * 3^1,所以6的所有因數為(1 + 1) * (1 + 1) = 4。
題目已經告訴我們1000000000中最多是192個因數。
所以會用到的質數一定小於192。
但這時候會有個問題,如果是給的值,是第193以後的質數怎麼辦?
因為質數只能被自己跟1整除,所以也不會被其他質數整除。
反正質數的因數就只有2個,所以質數也可以處理了。

老實說我一開始沒想到第193個質數之後的情況,
但寫出來的程式碼也誤打誤撞AC =.=,
可能測資過了,但程式碼還是要修。
後來終於找到一個想法跟我差不多的人寫得程式碼,
好厲害!他補完了我沒想到的部份:
連結

完整假設是這樣:
因為合數必定可以拆成兩個質數相乘。
如果存在一組質數,使得p1 * p2 > 1000000000,
而p1, p2之前的質數相乘都能 < 1000000000的話,
那麼在1000000000這些數中,
最多最多會用到的質數,就是比p1或p2小的質數,
這要怎麼證明呢?
因為合數n必定可以拆成a * b,
a跟b必有一數大於等於根號n,另一數小於等於根號n,
所以我們只要找等於根號n的那個質數即可。
大於等於根號n的質數不用找,因為a跟b都要考慮,
若一個是大於等於根號n,那另一個必定小於等於根號n,
如此,只要找到小於等於根號n的所有情況,
也就滿足所有情況了。
第3401個質數為31607,31607的平方為999002449,
第3402個質數為31627,31627的平方為1000267129。
所以最多最多會用到的質數就是第3401個質數為31607。

總結,
只要考慮能被前3402個質數整除即可,
再用質因數分解的方式去找所有質數即可。
若合數n最後不是等於1,表示存在一個質數p,
使得前3402個質數都無法整除此質數p。
這種情況也不用擔心,因為能整除質數數只有兩種,
1跟他自己。
所以再把答案 * 2即可。

/* ACM 294 Divisors
 * mythnc
 * 2011/11/17 12:13:07   
 * run time: 0.032
 */
#include <stdio.h>

#define MAXP  3402
#define TRUE  1
#define FALSE 0

typedef struct divisor {
    int number, count;
} Divisor;

typedef struct big {
    int number, sum;
} Big;

void findp(Divisor *);
Big maxn(Divisor *, int, int);
int dnumber(Divisor *, int);

int main(void)
{
    int head, tail;
    Big max;
    Divisor prime[MAXP];

    findp(prime);
    scanf("%*d");
    while (scanf("%d %d", &head, &tail) == 2) {
        max = maxn(prime, head, tail);
        printf("Between %d and %d, %d has a maximum of %d divisors.\n"
                , head, tail, max.number, max.sum);
    }
    return 0;
}

/* findp: find n primes */
void findp(Divisor *p)
{
    int count, i, j, flag;

    p[0].number = 2;
    count = 1;
    for (i = 3; count < MAXP; i += 2) {
        for (flag = TRUE, j = 3; j * j <= i; j++)
            if (i % j == 0) {
                flag = FALSE;
                break;
            }
        if (flag)
            p[count++].number = i;
    }
}

/* maxn: return the number which has the max divisors */
Big maxn(Divisor *p, int head, int tail)
{
    int i, number;
    Big max;

    max.number = head;
    max.sum = dnumber(p, head);
    for (i = head + 1; i < tail + 1; i++) {
        number = dnumber(p, i);
        if (max.sum < number) {
            max.sum = number;
            max.number = i;
        }
    }

    return max;
}

/* dnumber: return number of divisors of n */
int dnumber(Divisor *p, int n)
{
    int i, j, d;

    if (n == 1)
        return 1;

    for (i = 0; i < MAXP && n != 1; i++) {
        p[i].count = 0;
        while (n % p[i].number == 0) {
            n /= p[i].number;
            p[i].count++;
        }
    }

    for (d = 1, j = 0; j < i; j++)
        if (p[j].count != 0)
            d *= p[j].count + 1;

    if (n != 1)
        return d * 2;
    return d;
}

20111116

USACO Barn Repair

被婊到……
USACO的Greedy Algorithm說明文寫得根本不是Greedy Algorithm……
什麼求n = 5時先求n = 4的最佳解,害我想到DP去。
(因為n = 4的最佳解必定是n = 3的最佳解……根本就是從n = 1往上推的意思)

如果演算法正確還ok,
問題是DP的code到測資第5組就gg惹 =.=。
你說這不會讓人想殺人嗎?

其實也不用管什麼Greedy不Greedy的,
直接想作法還比較實際……
Greedy Algorithm這名詞真是抽象無比。

先對C做sort。
接著算出每個C之間的間距,一樣sort。
再計算只用1塊板子的長度Len,
當m = 1表示用1塊板子,Len就是答案。
m = 2時,Len要減去最大間距,
m = 3時,Len要減去最大間距與次大間距。
若m >= C,直接output C。
其實就這樣而已……

另外fopen本來就要fclose吧?
USACO的code從不做fclose,
無言。

盡信書不如無書。
一堆google別人想的解法還比USACO的好太多了 = =。
/*
ID: mythnc2
LANG: C
TASK: barn1
barn repair
2011/11/16 16:21:20   
*/
#include <stdio.h>
#include <stdlib.h>

#define MAXARY 200

int cmp(const void *, const void *);

int main (void)
{
    FILE *fin, *fout;
    int n, nc, i, sum;
    int ary[MAXARY], diff[MAXARY];

    fin = fopen("barn1.in", "r");
    fout = fopen("barn1.out", "w");

    fscanf(fin, "%d %*d %d", &n, &nc);
    for (i = 0; i < nc; i++)
        fscanf(fin, "%d", ary + i);

    if (n < nc) {
        qsort(ary, nc, sizeof(int), cmp);
        for (i = 0; i < nc - 1; i++)
            diff[i] = ary[i + 1] - ary[i] - 1;
        qsort(diff, nc - 1, sizeof(int), cmp);
        sum = ary[nc - 1] - ary[0] + 1;
        for (i = nc - 2; n > 1 ; i--, n--)
            sum -= diff[i];
        fprintf(fout, "%d\n", sum);
    }
    else
        fprintf(fout, "%d\n", nc);

    fclose(fin);
    fclose(fout);
    return 0;
}

/* cmp: function of qsort argument */
int cmp(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;
}

ACM 492 Pig-Latin

一個字元一個字元慢慢吃,
再判斷是否是字母。
是就存起來,
當遇到非字母時,就output改過的單字。

有一點stack的味道。

/* ACM 492 Pig-Latin
 * mythnc
 * 2011/11/16 11:50:51   
 * run time: 0.028
 */
#include <stdio.h>
#include <ctype.h>

#define MAXCHAR 1000

void print(char *);

int main(void)
{
    char word[MAXCHAR];
    int c, i;

    i = 0;
    while ((c = getchar()) != EOF)
        if (isalpha(c))
            word[i++] = c;
        else {
            if (i != 0) {
                word[i] = '\0';
                print(word);
                i = 0;
            }
            putchar(c);
        }
    return 0;
}

/* print: print out word in pig-latin form */
void print(char *w)
{
    switch (w[0]) {
        case 'A':
        case 'a':
        case 'E':
        case 'e':
        case 'I':
        case 'i':
        case 'O':
        case 'o':
        case 'U':
        case 'u':
            printf("%say", w);
            break;
        default:
            printf("%s%cay", w + 1, w[0]);
    }
}

ACM 568 Just the Facts

乘法進位問題。
每次補0就要移到下一位,
下一位的數字又是取決於乘法進位。
所以直接只用最後幾個位數做乘法是不行的,
因為只要有0就要考慮下一個數。
15!就已經有2個0了。

所以採取的作法是半大數,只計算最後100位數,
至於為什麼是100位數呢?
我只是取一個不會被乘法進位問題干涉到的值,
或許50位數應該也ok吧?可以試試,
或是有其他比較好的作法。

/* ACM 568 Just the Facts
 * mythnc
 * 2011/11/16 10:06:34   
 * run time: 0.016
 */
#include <stdio.h>

#define MAXN 10001
#define MAXD 100

void init(int *, char *);

int main(void)
{
    int n;
    int digit[MAXD] = { 0 };
    char save[MAXN];

    init(digit, save);
    while (scanf("%d", &n) == 1)
        printf("%5d -> %c\n", n, save[n]);

    return 0;
}

/* init: precalculate answer,
 * save last non-zero in array save */
void init(int *d, char *s)
{
    int i, j, k;

    s[0] = '0';
    d[0] = 1;
    for (i = 1; i < MAXN; i++) {
        for (j = 0; j < MAXD; j++)
            d[j] *= i;
        /* carry */
        for (j = 0; j < MAXD - 1; j++)
            if (d[j] > 9) {
                d[j + 1] += d[j] / 10;
                d[j] %= 10;
            }
        /* remove zero */
        if (i > 4) {
            for (j = 0; d[j] == 0; j++)
                ;
            for (k = 0; j < MAXD; j++, k++)
                d[k] = d[j];
            for (; k < MAXD; k++)
                d[k] = 0;
        }
        /* copy last digit to s */
        s[i] = d[0] + '0';
    }
}

20111115

ACM 572 Oil Deposits

ACM10189類似。

(1) 找到@之後,把@轉變為*,
(2) 並再找其四面八方的是否存在@,
反覆(1)跟(2)。

一次傳超過4個變數有點討厭,
所以就用全域了……。

/* ACM 572 Oil Deposits
 * mythnc
 * 2011/11/29 15:17:44   
 * version 2  
 * run time: 0.004
 */
#include <stdio.h>

#define MAXROW 100
#define MAXCOL 101
#define TRUE   1
#define FALSE  0

int row, col;

int sweep(char (*g)[MAXCOL]);
void clear(char (*g)[MAXCOL], int i, int j);

int main(void)
{
    char grid[MAXROW][MAXCOL];
    int i;

    while (scanf("%d %d", &row, &col) && row != 0) {
        for (i = 0; i < row; i++)
            scanf("%s", grid[i]);

        printf("%d\n", sweep(grid));
    }
    return 0;
}

/* sweep: sweep and return distinct oil number */
int sweep(char (*g)[MAXCOL])
{
    int count, i, j;

    for (count = i = 0; i < row; i++)
        for (j = 0; j < col; j++)
            if (g[i][j] == '@') {
                count++;
                clear(g, i, j);
            }

    return count;
}

/* clear: clear '@' to '*' */
void clear(char (*g)[MAXCOL], int i, int j)
{
    int mover[] = {1, 1, 1, 0, -1, -1, -1, 0};
    int movec[] = {-1, 0, 1, 1, 1, 0, -1, -1};
    int k, x, y;

    g[i][j] = '*';
    for (k = 0; k < 8; k++) {
        x = i + mover[k];
        y = j + movec[k];
        if (x >= 0 && x < row && y >= 0 && y < col
            && g[x][y] == '@')
            clear(g, x, y);
    }
}

ACM 438 The Circumference of the Circle

給三點,求其外接圓周長。

已知3點ABC,相對應的外接圓直徑為,
abc / 2 * area。
area由海龍公式可得,
根號[s * (s - a) * (s - b) * (s - c)],
s為周長 / 2。
代入化簡可得圓周長為:
pi * 2abc / 根號的[(a + b + c) * (b + c - a) * (a + b - c ) * (a + c - b)]

/* ACM 438 The Circumference of the Circle
 * mythnc
 * 2011/11/15 12:26:59   
 * run time: 0.012
 */
#include <stdio.h>
#include <math.h>

#define MAXP 3

typedef struct coordinates {
    double x;
    double y;
} Coordinates;

double diameter(Coordinates *);
double distance(Coordinates, Coordinates);

int main(void)
{
    Coordinates point[MAXP];
    int i;
    double const pi = 3.141592653589793;

    while (scanf("%lf %lf", &point[0].x, &point[0].y) == 2) {
        for (i = 1; i < MAXP; i++)
            scanf("%lf %lf", &point[i].x, &point[i].y);
        printf("%.2lf\n", pi * diameter(point));
    }
    return 0;
}

/* circum: return the diameter value */
double diameter(Coordinates *point)
{
    double a, b, c;

    a = distance(point[0], point[1]);
    b = distance(point[1], point[2]);
    c = distance(point[2], point[0]);

    return 2 * a * b * c
           / sqrt((a + b + c) * (b + c - a) * (a + c - b) * (a + b - c));
}

/* distance: return the distance between p1 and p2 */
double distance(Coordinates p1, Coordinates p2)
{
    return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}

20111114

USACO Mixing Milk

原來這種算則就叫greedy algorithm……

每次找最便宜的milk,
接著確認amount供應是否正常,
反覆此二步驟,直到滿足amount量,
輸出金額。

先做sort好像會比較好一點……

/*
ID: mythnc2
LANG: C
TASK: milk
Mixing Milk
2011/11/14 21:57:02
*/
#include <stdio.h>

#define MAXF 5000

typedef struct provider {
    int price;
    int amount;
} Provider;

int findmin(Provider *, int);
int checkamount(Provider, int);

int main (void)
{
    FILE *fin, *fout;
    int amount, farmer, i, sum, min, quan;
    Provider milk[MAXF];

    fin = fopen("milk.in", "r");
    fout = fopen("milk.out", "w");

    fscanf(fin, "%d %d", &amount, &farmer);
    for (i = 0; i < farmer; i++)
        fscanf(fin, "%d %d", &milk[i].price, &milk[i].amount);

    for (sum = 0; amount; milk[min].price = 1001) {
        min = findmin(milk, farmer);
        quan = checkamount(milk[min], amount);
        sum += quan * milk[min].price;
        amount -= quan;
    }

    fprintf(fout, "%d\n", sum);

    return 0;
}

/* findmin: return the minimum milk.price element */
int findmin(Provider *milk, int n)
{
    int i, min;

    for (i = 1, min = 0; i < n; i++)
        if (milk[i].price < milk[min].price)
            min = i;

    return min;
}

/* checkamount: return the milk.amount provide
 * actually */
int checkamount(Provider milk, int amount)
{
    if (milk.amount > amount)
        return amount;

    return milk.amount;
}

USACO Dual Palindromes

Palindromic Squares類似。
要注意S雖然有範圍,
但主要是求出N的個數,
個數有可能超過S的範圍。

/*
ID: mythnc2
LANG: C
TASK: dualpal
Dual Palindromes
2011/11/14 20:30:40   
*/
#include <stdio.h>
#include <string.h>

#define MAXCHAR 15
#define TRUE    1
#define FALSE   0

void conversion(int, int, char *);
int pal(char *);

int main (void)
{
    FILE *fin, *fout;
    int n, s, i, j, count;
    char t[MAXCHAR];

    fin = fopen("dualpal.in", "r");
    fout = fopen("dualpal.out", "w");

    fscanf(fin, "%d %d", &n, &s);

    for (i = s + 1; n; i++)
        for (count = 0, j = 2; j < 11; j++) {
            conversion(j, i, t);
            if (pal(t))
                count++;
            if (count > 1) {
                fprintf(fout, "%d\n", i);
                n--;
                break;
            }
        }

    return 0;
}

/* conversion: conver n in base of b,
 * save in t */
void conversion(int b, int n, char *t)
{
    int i, mod;
    char map[] = "0123456789";

    for (i = 0; n; i++) {
        t[i] = map[n % b];
        n /= b;
    }
    t[i] = '\0';
}

/* pal: if t is palindrome return TRUE,
 * else return FALSE */
int pal(char *t)
{
    int i, j;

    for (i = 0, j = strlen(t) - 1; i < j; i++, j--)
        if (t[i] != t[j])
            return FALSE;

    return TRUE;
}

ACM 490 Rotating Sentences

順時針轉90度角。
每行的\n不要處理。
範例測資很怪,h後面有space,但r跟e後面就沒有……
我寫的AC code有補space。

/* ACM 490 Rotating Sentences
 * mythnc
 * 2011/11/14 19:22:42   
 * run time: 0.008
 */
#include <stdio.h>
#include <string.h>

#define MAXLINE 100
#define MAXCHAR 105

int maxlen(char (*seq)[MAXCHAR], int n);

int main(void)
{
    char seq[MAXLINE][MAXCHAR];
    int count, i, j;

    count = 0;
    while (fgets(seq[count], MAXCHAR, stdin))
        count++;

    for (i = 0; i < maxlen(seq, count); i++, putchar('\n'))
        for (j = count - 1; j > -1; j--)
            if (i >= strlen(seq[j]) - 1)
                putchar(' ');
            else
                printf("%c", seq[j][i]);

    return 0;
}

/* maxlen: return max string length */
int maxlen(char (*seq)[MAXCHAR], int n)
{
    int i, max;

    for (i = max = 0; i < n; i++)
        if (max < strlen(seq[i]))
            max = strlen(seq[i]);

    return max - 1;  /* we don't need '\n' */
}

ACM 10006 Carmichael Numbers

一開始以為只要算prime就好了,真是好天真,
馬上得到WA一枚。

Carmichael number的意思:
某數經由Fermat Test算出為質數,
但實際上為合數的數,就稱為Carmichael number。

也就是判斷n是否由Fermat Test算出來是質數,
但實際上是合數即可。

Fermat Test是關鍵,因為要算mod,
雖然跟ACM374,但我那題的解法在這題卻不適用。
一個比較好的算則是mod的分配律:
假設 (a * b) % m,可以拆為:
(a % m) * (b % m)。
同理,a^2 % m可以拆為,
(a % m) * (a % m)。
如此就可以把a^n一路往下拆,拆到n為1或0為止。
然而最大數為64998 ^ 2為4224740004,
所以要用unsigned。

有兩種作法,先算出1到65000的Carmichael number,
若測資為這些數,可直接做輸出。

第二種方法,直接做,但演算法要正確且快速,否則會TL。
判斷數字n是否是質數,是質數的情況下,必不為Carmichael number,
若n滿足Fermat Test,要再判斷n是否為質數。
C語言的&&運算子為short-circuit evaluation,
所以質數判斷放前面,Fermat Test判斷放後面,
會比較快。

/* ACM 10006 Carmichael Numbers
 * mythnc
 * 2011/11/14 16:53:51   
 * run time: 0.144
 */
#include <stdio.h>

#define FALSE 0
#define TRUE  1

int prime(int);
int fermat(int);
unsigned int powermod(int a, int n, int d);

int main(void)
{
    int n;

    while (scanf("%d", &n) && n != 0)
        if (!prime(n) && fermat(n))
            printf("The number %d is a Carmichael number.\n", n);
        else
            printf("%d is normal.\n", n);

    return 0;
}

/* fermat: do Fermat test,
 * if n is prime return TRUE,
 * else return FALSE.
 * WARNING: it means in Fermat test is a prime,
 * maybe it isn't really a prime */
int fermat(int n)
{
    int i;

    for (i = 2; i < n; i++)
        if (powermod(i, n, n) != i)
            return FALSE;

    return TRUE;
}

/* powermod: calculate mod value,
 * in recursive method,
 * return big mod number */
unsigned int powermod(int a, int n, int d)
{
    unsigned int x;

    if (n == 0)
        return 1;
    if (n == 1)
        return a % d;
    
    x = powermod(a, n / 2, d);
    x = x * x % d;
    if (n % 2 == 0)
        return x;
    else
        return x * a % d;
}

/* prime: return TRUE if n is prime,
 * else return FALSE */
int prime(int n)
{
    int i;

    for (i = 3; i * i <= n; i++)
        if (n % i == 0)
            return FALSE;

    return TRUE;
}

ACM相關網站

有很多人都在寫ACM,有時候想不到好的演算法時,看看別人怎麼想,別人怎麼解題,也是一種學習。

國內
翼世界夢想領域
http://maplewing.blogspot.com/

Ya-Lin Huang's ACM Problemset
http://yalin.tw/acm.php

調和的靈感
http://chchwy.blogspot.com/p/my-solved-acm-problems-list.html

Robert Anderson's Blog
http://www.wretch.cc/blog/robertanders

摸索C語言
http://mypaper.pchome.com.tw/zerojudge
http://mypaper.pchome.com.tw/iustlovefish

ACM解題筆記
http://kos74185foracm.blogspot.com/

The Tagtraum House
http://tagtraumhouse.blogspot.com/

國外
algorithmist
http://www.algorithmist.com/index.php/Category:UVa_Online_Judge

World of Seven
http://www.comp.nus.edu.sg/~stevenha/programming/acmoj.html

ACM 138 Street Numbers

直接做會算到天荒地老,第7組電腦跑了一個小時還跑不出來,
暴力是不行滴!

google或是看uva討論板,都是寫些我看不懂的算則(遮臉),
最後在這裡
終於看到一個看得懂的算則了。
很直觀的想法,但我竟然沒想到 orz。

大意就是門牌號碼i往上遞增時,左邊的總數加第i - 1個號碼,
右邊的總數先減去第i個號碼,再從j + 1開始往上加,直到找到滿10組解。
(ps 要先找到第一組解才行)

/* ACM 138 Street Numbers
 * mythnc
 * 2011/11/14 07:54:27   
 * run time: 0.3
 */
#include <>

int main(void)
{
    long long i, j, pre, next;
    int count;

    printf("%10d%10d\n", 6, 8);
    pre = next = 15;
    j = 9;
    for (i = 7, count = 1; count < 10; i++) {
            pre += i - 1;
            next -= i;
        for (; next < pre; j++)
            next += j;
        if (next == pre) {
            printf("%10lld%10lld\n", i, j - 1);
            count++;
        }
    }
    return 0;
}

ACM 10302 Summation of Polynomials

算立方和。
其實我看不懂他題目在推導蝦米,
{n}他寫得真怪……

/* ACM 10302 Summation of Polynomials
 * mythnc
 * 2011/11/14 07:28:09   
 * run time: 0.016
 */
#include <stdio.h>

int main(void)
{
    long long n;

    while (scanf("%lld", &n) == 1)
        printf("%lld\n", n * n * (n + 1) * (n + 1) / 4);

    return 0;
}

20111113

ACM 10222 Decode the Mad man

ACM10082
做mapping。

/* ACM 10222 Decode the Mad man
 * mythnc
 * 2011/11/13 14:52:11   
 * run time: 0.004
 */
#include <stdio.h>
#include <string.h>

int main(void)
{
    char key[] = "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./";
    int map[256];
    int i, c;
    /* mapping */
    for (i = 0; i < strlen(key); i++)
        map[key[i]] = key[i - 2];

    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n') {
            putchar(c);
            continue;
        }
        if (c >= 'A' && c <= 'Z')  /* upper case */
            c = c - 'A' + 'a';
        putchar(map[c]);
    }
    return 0;
}

ACM 10106 Product

大數運算,乘法。

每次都死在邊界問題啊……
0 * 0 = 0

/* ACM 10106 Product */
/* mythnc
 * 2011/11/13 13:12:16   
 * run time: 0.008
 */
#include <stdio.h>
#include <string.h>

#define MAXCHAR 251
#define MAXD    500

void init(int *);
void reverse(char *);
void mul(char *, char *, int *);
int ctoi(char);
void print(int *);

int main(void)
{
    char x[MAXCHAR], y[MAXCHAR];
    int product[MAXD];

    while (scanf("%s\n%s", x, y) == 2) {
        init(product);
        reverse(x);
        reverse(y);
        mul(x, y, product);
        print(product);
    }
    return 0;
}

/* init: initialize product */
void init(int *p)
{
    memset(p, 0, MAXD * sizeof(int));
}

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

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

/* mul: do x * y save result in p */
void mul(char *x, char *y, int *p)
{
    int i, j, lenx, leny;

    lenx = strlen(x);
    leny = strlen(y);
    for (i = 0; i < lenx; i++)
        for (j = 0; j < leny; j++)
            p[i + j] += ctoi(x[i]) * ctoi(y[j]);

    /* carry */
    for (i = 0; i < lenx + leny - 1; i++)
        if (p[i] > 9) {
            p[i + 1] += p[i] / 10;
            p[i] %= 10;
        }
}

/* ctoi: return char to int */
int ctoi(char c)
{
    return c - '0';
}

/* print: print p from high to low */
void print(int *p)
{
    int i;

    for (i = MAXD - 1; p[i] == 0 && i > 0; i--)
        ;

    for (i; i > -1; i--)
        printf("%d", p[i]);
    putchar('\n');
}

20111112

單行不定筆資料

假設每行的資料並非相同,也沒有固定,該如何處理此類問題?
例如題目要求加總每行整數的和,每一行都是一筆測資,資料數不固定。
input
1 2 3
2 3 5 7
3 3
output
6
17
6

第一種作法:先用fgets讀取整行資料,再用strtok做分解。
#include <stdio.h>
#include <string.h>

#define MAX 100

int main(void)
{
    char s[MAX];
    char *pt;
    int x, sum;

    while (fgets(s, MAX, stdin)) {
        pt = strtok(s, " ");
        sum = 0;
        while (pt) {
            sscanf(pt, "%d", &x);
            sum += x;
            pt = strtok(NULL, " ");
        }
        printf("%d\n", sum);
    }

    return 0;
}


第二種作法,是我在網路上看到的程式碼,比較直觀,也比較好實做。
利用scanf()搭配%s%c的特性,再用sscanf()處理,這種作法也是K&R的其中一題exercise。
#include <stdio.h>

#define MAX 10

int main(void)
{
    char s[MAX];
    char c;
    int x, sum;

    sum = 0;
    while (scanf("%s%c", s, &c) == 2) {
        sscanf(s, "%d", &x);
        sum += x;
        if (c == '\n') {
            printf("%d\n", sum);
            sum = 0;
        }
    }

    return 0;
}

scanf("%s%c", s, &c) == 2的意思是一次讀取一個整數(視為字串),
再加上整數後面的space或是newline。

如此,類似的題目,例如calculator,不定筆字串,皆可以用這種方式處理。

ACM 706 LC-Display

寫得滿暴力的。
關鍵在於,單單輸出一個數字時很簡單,
但是一次輸出多個數字,就是同時處理多個數字。

也就是第一行要輸出每個數字的第一行,
二行要輸出每個數字的第二行。

所以算則就是這樣,
依照數字與行數做相對應輸出。

輸出的字元可以化簡為幾種:
(1)空白
(2)最右邊為「|」
(3)最左邊為「|」
(4)最左跟最右邊為「|」
(5)中間為「-」

/* ACM 706 LC-Display
 * mythnc
 * 2011/11/12 21:54:59   
 * run time: 0.032
 */
#include <stdio.h>

#define MAXCHAR 10

void printn(char, int, int);
void space(int);
void right(int);
void middle(int);
void left(int);
void landr(int);

int main(void)
{
    char str[MAXCHAR];
    int n, i, j;

    while (scanf("%d %s", &n, str) && n != 0) {
        for (i = 0; i < 2 * n + 3; i++) {       /* line */
            for (j = 0; str[j] != '\0'; j++) {  /* number */
                if (j > 0)
                    putchar(' ');
                printn(str[j], i, n);
            }
            putchar('\n');
        }
        putchar('\n');
    }
    return 0;
}

/* printn: depend on row value and char c to do output */
void printn(char c, int row, int n)
{
    int col, head, mid, tail;

    col = n + 2;
    head = 0;
    mid = (2 * n + 3) / 2;
    tail = 2 * n + 2;   /* (2 * n + 3) - 1 */
    switch (c) {
        case '1':
            if (row == head || row == mid || row == tail)
                space(col);
            else
                right(col);
            break;
        case '2':
            if (row == head || row == mid || row == tail)
                middle(col);
            else if (row < mid)
                right(col);
            else
                left(col);
            break;
        case '3':
            if (row == head || row == mid || row == tail)
                middle(col);
            else
                right(col);
            break;
        case '4':
            if (row == head || row == tail)
                space(col);
            else if (row < mid)
                landr(col);
            else if (row == mid)
                middle(col);
            else
            right(col);
            break;
        case '5':
            if (row == head || row == mid || row == tail)
                middle(col);
            else if (row < mid)
                left(col);
            else
                right(col);
            break;
        case '6':
            if (row == head || row == mid || row == tail)
                middle(col);
            else if (row < mid)
                left(col);
            else
                landr(col);
            break;
        case '7':
            if (row == head)
                middle(col);
            else if (row == mid || row == tail)
                space(col);
            else
                right(col);
            break;
        case '8':
            if (row == head || row == mid || row == tail)
                middle(col);
            else
                landr(col);
            break;
        case '9':
            if (row == head || row == mid || row == tail)
                middle(col);
            else if (row < mid)
                landr(col);
            else
                right(col);
            break;
        case '0':
            if (row == head || row == tail)
                middle(col);
            else if (row == mid)
                space(col);
            else
                landr(col);
    }
}

/* space: print ' ' */
void space(int n)
{
    while (n--)
        putchar(' ');
}

/* right: print '|' in the rightmost */
void right(int n)
{
    int i;

    for (i = 0; i < n - 1; i++)
        putchar(' ');
    putchar('|');
}

/* middle: print '-' in middle */
void middle(int n)
{
    int i;

    putchar(' ');
    for (i = 1; i < n - 1; i++)
        putchar('-');
    putchar(' ');
}

/* left: print '|' in leftmost */
void left(int n)
{
    int i;

    putchar('|');
    for (i = 1; i < n; i++)
        putchar(' ');
}

/* landr: print '|' in leftmost and rightmost */
void landr(int n)
{
    int i;

    putchar('|');
    for (i = 1; i < n - 1; i++)
        putchar(' ');
    putchar('|');
}