机考 出牌顺序 dfs

题目:

给定一堆牌,每张牌都有数字和颜色,选手从中选出一张后,只能再挑选颜色或者数字和上一张相同的牌,否则不能抽选

选手应该怎么选才能使得抽选的次数最大,并且输入这个最大次数

输入

第一行 牌的数值n (1<=n<=10)

第二行 牌的颜色(r,l,g,t四种颜色表示)

输入

抽取的最大次数

示例:

输入

4 1 3 5 4

r g t r n

输出

3


这个题当时尝试了50分钟没搞出来,因为之前没做过广度优先法的题,没这个思维。后来看了下网友的思路

参考LeetCode547,本质就是BFS进行图的遍历,找到最大连通的子图的节点数
随手写了一下代码,没有进一步进行优化,但是应该是OK的。

def findMaxCards(cards):
    '''
    广度优先搜索-队列
    图的遍历,一个节点出对,相连通的节点入对,当队列为空,则表示已经遍历完了一个连通的子图
    输入:卡片的列表,内容为数字+颜色,如['4r','5r','4n','3t','1g']
    输出:最大连通子图的卡牌数量
    '''
    # 先将输入列表转化成邻接矩阵,若两个节点数字相同或颜色相同,则连通
    n = len(cards)
    matrix = [[0 for _ in range(n)] for _ in range(n)]
    for i in range(n):
        for j in range(n):
            if cards[i][0]==cards[j][0] or cards[i][1]==cards[j][1]:
                matrix[i][j]=1
    # BFS进行广度搜索,队列实现
    visited = [0 for _ in range(n)] # 记录访问过的卡片
    max_res = 0 # 记录最大的连通子图的节点个数
    
    temp = 0 # 记录每个连通子图的节点个数
    for card_idx in range(n):
        if visited[card_idx]==0:
            queue = [card_idx]
            visited[card_idx]=1
        else:
            continue
        
        while queue:
            i = queue.pop(0)
            temp+=1
            for j in range(n):
                if matrix[i][j]==1 and visited[j]==0 and i!=j:
                    queue.append(j)
                    visited[j]=1
        max_res = max(max_res,temp)
        temp = 0
    return max_res

cards = ['4r','5r','4n','3t','1g']
print(findMaxCards(cards))

我没看懂,但是我感觉算的并不对


我按照这个思路,给了一个例子:

1 2 3 3 5 5

a b b c b a

序号

0

1

2

3

4

5

数字

1

2

3

3

5

5

花色

a

b

b

c

b

a

我需要先把这些牌之间的关系找出来,就是按数字比相等或者按照花色比相同

record

0

1

2

3

4

5

0

1

0

0

0

0

1

1

0

1

1

0

1

0

2

0

1

1

1

1

0

3

0

0

1

1

0

0

4

0

1

1

0

1

1

5

1

0

0

0

1

1

然后再记录一个map,每次出一张牌,都要将这个牌的map位置1,这样下次就不再找这个牌了

map

0

1

2

3

4

5

0

0

0

0

0

0

思路就是,假设从每一个牌开始出,计算后面在这个牌为前的所有可能性,然后可以接的牌,再作为前一个牌,去找接下来的其他牌的可能性.......依次查下去,这肯定需要递归的思想。

麻烦的在于,牌出了一次就不能再出了,但是我先按照一个排序查找后,置map,又不能影响后面可能性的map,这就需要每次从一个情况往下找时,用自身独立的map,不影响其他情况。

每次找到一张牌的可能性后,都要计数+1再加上下一次查找的值,返回。

比如说,我先出第一张牌,置map[0]=1;再找下一张牌的可能性, 根据record[0][i] (i!=0)发现只有i=5的可能性; count++

然后5再找下一张,置map[5]=1;根据record[0][i] (i!=0)发现只有i=4的可能性; count++

然后4再找下一张,置map[4]=1;根据record[0][i] (i!=0)发现有i=1;i=2的可能性;(i=4不能等于自己,和i=5已经用过了,所以排除) count++

然后1再找下一张,同时2再找下一张。。。(这就需要同时查两种情况了,算出来的可能性,取最大值返回)

.........

这样到最后,map也满了,或者找不到record == 1的情况了,就返回0,就结束了这个路径的递归查找了。

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

#define get_max(m,n) (m>n ? m : n)

char record[10][10] = {0};      // 记录牌和牌之间的关系
int card_num = 0;
// pOutNum是这次找到的lastNum接这个牌的最优解
int findMatch(char *pMap, int lastNum, int targetNum){
    char map_temp[10] = {0};
    int num[10] = {0};
    int max_num = 0;

    memcpy(map_temp, pMap, 10);
    for(int i=0; i<card_num; i++){
        if(map_temp[i] == 0 && record[lastNum][i] && lastNum != i){
            map_temp[i] = 1;
            num[i] = 1 + findMatch(map_temp, i, targetNum-1);
            max_num = get_max(max_num, num[i]);
            if(targetNum == max_num){
                printf("%d ", i);
                break;
            }
        }
    }
    return max_num;
}
int main(){
    // char card[10][2] = {{'1', 'a'}, {'2', 'b'}, {'3', 'a'}, {'3', 'c'}, {'5', 'b'}, {'5', 'a'}};
    char card[10][2] = {0};
    char map[10] = {0};     // 记录路径是否用过某个牌
    int max_num = 0;
    int num[10] = {0};            // 从i开始的牌的最大值

    memset(record, 0, sizeof(record));
    while(scanf("%[0-9]", &card[card_num][0]) != 0){
        card_num++;
        if(getchar() == '\n'){
            break;
        }
    }
    printf("card_num:%d\n", card_num);
    for(int i=0; i<card_num; i++){
        scanf("%c ", &card[i][1]);
    }
    for(int i=0; i<card_num; i++){
        printf("card[%d]: %c %c ", i, card[i][0], card[i][1]);
    }
    printf("\n");

    for(int i=0; i<card_num; i++){
        for(int k=0; k<card_num; k++){
            if(card[i][0] == card[k][0] || card[i][1] == card[k][1]){
                record[i][k] = 1;
            }
            printf("%d ", record[i][k]);
        }
        printf("\n");
    }
    for(int i=0; i<card_num; i++){
        char map_temp[10] = {0};
        memcpy(map_temp, map, 10);
        map_temp[i] = 1;
        printf("\nnum:%d ", i);
        num[i] = findMatch(map_temp, i, card_num);
        max_num = get_max(max_num, num[i]);
    }
    
    for(int i=0; i<card_num; i++){
        if(max_num == num[i]){
            char map_temp[10] = {0};
            memcpy(map_temp, map, 10);
            map_temp[i] = 1;
            printf("\nnum---:%d ", i);
            findMatch(map_temp, i, num[i]);
        }
    }
    printf("\nmax:%d\n", max_num+1);  // 上面查到的是以某一张牌开始的后面找到的牌数,还应加上自身
}

上面代码的targetNum其实就是为了找出路径,不然就只要找到返回值的最大值就可以了。

找出经历过的路径也比较麻烦,因为递归的时候并不知道哪个才是最大的可能性,得都找到最大值后,再找到第一张开始的,再来一遍

输入:

1 2 3 3 5 5

a b b c b a

输出:

card_num:6

card[0]: 1 a card[1]: 2 b card[2]: 3 b card[3]: 3 c card[4]: 5 b card[5]: 5 a

1 0 0 0 0 1

0 1 1 0 1 0

0 1 1 1 1 0

0 0 1 1 0 0

0 1 1 0 1 1

1 0 0 0 1 1

num:0

num:1

num:2

num:3

num:4

num:5

num---:0 3 2 1 4 5

num---:3 0 5 4 1 2

max:6

可以看到从牌0和牌3开始,路径都最长

序号

0

1

2

3

4

5

数字

1

2

3

3

5

5

花色

a

b

b

c

b

a

0 5 4 1 2 3

3 2 1 4 5 0


我一直对算法类的东西感觉很困难,递归啊之类的,还是得多练

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值