算法题-游戏类问题

24.Algorithm Gossip: 洗扑克牌(乱数排列)

说明

洗扑克牌的原理其实与乱数排列是相同的,都是将一组数字(例如1~N)打乱重新排列,只不过洗扑克牌多了一个花色判断的动作而已。

解法

初学者通常会直接想到,随机产生1~N的乱数并将之存入阵列中,后来产生的乱数存入阵列前必须先检查阵列中是否已有重复的数字,如果有这个数就不存入,再重新产生下一个数,运气不好的话,重复的次数就会很多,程序的执行速度就很慢了,这不是一个好方法。

以1~52的乱数排列为例好了,可以将阵列先依序由1到52填入,然后使用一个回圈走访阵列,并随机产生1~52的乱数,将产生的乱数当作索引取出阵列值,并与目前阵列走访到的值相交换,如此就不用担心乱数重复的问题了,阵列走访完毕后,所有的数字也就重新排列了。

至于如何判断花色?这只是除法的问题而已,取商数判断花色,取余数判断数字,您可以直接看程序比较清楚。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 52
int main(void) {
    int poker[N + 1];
    int i, j, tmp, remain;
    // 初始化阵列
    for(i = 1; i <= N; i++)
        poker[i] = i; 
    srand(time(0));
    // 洗牌
    for(i = 1; i <= N; i++) {
        j = rand() % 52 + 1;
        tmp = poker[i];
        poker[i] = poker[j]; 
        poker[j] = tmp; 
    }
    for(i = 1; i <= N; i++) {
        // 判断花色
        switch((poker[i]-1) / 13) { 
            case 0: 
                printf(""); break;
            case 1: 
                printf(""); break;
            case 2: 
                printf(""); break;
            case 3: 
                printf(""); break;
        } 
        // 扑克牌数字
        remain = poker[i] % 13;
        switch(remain) { 
            case 0: 
                printf("K "); break;
            case 12: 
                printf("Q "); break;
            case 11: 
                printf("J "); break;
            default: 
                printf("%d ", remain); break;
        } 
        if(i % 13 == 0)
            printf("\n");
    } 
    return 0;
}

25.Algorithm Gossip: Craps赌博游戏

说明一个简单的赌博游戏,游戏规则如下:玩家掷两个骰子,点数为1到6,如果第一次点数和为7或11,则玩家胜,如果点数和为2、3或12,则玩家输,如果和 为其它点数,则记录第一次的点数和,然后继续掷骰,直至点数和等于第一次掷出的点数和,则玩家胜,如果在这之前掷出了点数和为7,则玩家输。

解法 规则看来有些复杂,但是其实只要使用switch配合if条件判断来撰写即可,小心不要弄错胜负顺序即可。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define WON 0
#define LOST 1
#define CONTINUE 2
int rollDice() { 
    return (rand() % 6) + (rand() % 6) + 2;
}
int main(void) {
    int firstRoll = 1;
    int gameStatus = CONTINUE;
    int die1, die2, sumOfDice;
    int firstPoint = 0;
    char c;
    srand(time(0));
    printf("Craps赌博游戏,按Enter键开始游戏****");
    while(1) {
        getchar();
        if(firstRoll) {
            sumOfDice = rollDice();
            printf("\n玩家掷出点数和:%d\n", sumOfDice);
            switch(sumOfDice) {
                case 7: case 11:
                    gameStatus = WON; break;
                case 2: case 3: case 12:
                    gameStatus = LOST; break;
                default:
                    firstRoll = 0;
                    gameStatus = CONTINUE;
                    firstPoint = sumOfDice;
                    break;
            }
        }
        else {
            sumOfDice = rollDice();
            printf("\n玩家掷出点数和:%d\n", sumOfDice);
            if(sumOfDice == firstPoint)
                gameStatus = WON;
            else if(sumOfDice == 7)
                gameStatus = LOST;
        }
        if(gameStatus == CONTINUE)
            puts("未分胜负,再掷一次****\n");
        else {
            if(gameStatus == WON)
                puts("玩家胜");
            else
                puts("玩家输");
            printf("再玩一次?");
            scanf("%c", &c);
            if(c == 'n') {
                puts("游戏结束");
                break;
            }
            firstRoll = 1;
        }
    }
    return 0;
}

26.Algorithm Gossip: 约瑟夫问题(Josephus Problem)

说明据说着名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人 开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

解法约瑟夫问题可用代数分析来求解,将这个问题扩大好了,假设现在您与m个朋友不幸参与了这个游戏,您要如何保护您与您的朋友?只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈内圈是排列顺序,而外圈是自杀顺序,如下图所示:

clip_image001

使用程序来求解的话,只要将阵列当作环状来处理就可以了,在阵列中由计数1开始,每找到三个无资料区就填入一个计数,直而计数达41为止,然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列,41个人而报数3的约琴夫排列如下所示:

14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23

由上可知,最后一个自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道约琴夫与他的朋友并没有遵守游戏规则了。

#include <stdio.h> 
#include <stdlib.h> 
#define N 41 
void josephus(int*, int);
int main(void) { 
    printf("\n\n您想要救多少人?");     
    int alive;
    scanf("%d", &alive); 

    int man[N] = {0}; 
    josephus(man, alive);
    printf("\n約琴夫排列:"); 
    int i;
    for(i = 0; i < N; i++) 
        printf("%d ", man[i]); 
    printf("\nL表示這%d人要放的位置:\n", alive); 
    for(i = 0; i < N; i++) { 
        printf(man[i] <= (N - alive) ? "D" : "L");
        printf((i+1) % 5 == 0 ? "  " : "");
    } 
    printf("\n"); 
    return 0; 
} 
void josephus(int* man, int per) {
    int count;
    int pos;
    for(count = 1, pos = -1; count <= N; count++) {
        int i = 0;
        while(i != per) { 
            pos = (pos+1) % N;  // 環狀處理 
            if(man[pos] == 0) 
                i++; 
        } 
        man[pos] = count; 
    }     
}
posted @ 2012-11-13 08:29  Mr.Rico  阅读(415)  评论(0编辑  收藏  举报