蓝桥杯 分考场问题

题目描述

n个人参加某项特殊考试。

为了公平,要求任何连两个认识的人不能分到同一个考场。

求最少需要多少个考场。

输入描述

输入格式:

第一行,一个整数n(1<=n<=100),表示参加考试的人数。

第二行,一个整数m,表示接下来有m行数据。

以下m行每行的格式为:两个整数a,b,用空格分开(1<=a,b<=n)表示a个人与第b个人认识。

输出描述

输出一行一个整数,表示最少分几个考场。

示例:输入

5

8

1 2

1 3

1 4

2 3

2 4

2 5

3 4 

4 5

 输出

4

思路

我想分享一下自己初次尝试的解法,方便大家理解。

我们可以创建一个二维关系矩阵nums,如果a跟b认识,则nums[a][b]=1,不认识,则nums[a][b]=0。创建一个rooms[i][j],其中i表示第i个房间,j表示这个房间的第j个人。刚开始我们将第一个人先放进房间,然后从第2个人开始遍历到第n个人,每次从第一个房间遍历,如果这个人和这个房间有人认识,那么往下遍历,如果每个房间都有认识的,那就再开个房间。

以下是我初次尝试的写法,只有40%通过率,我会分析我的错误原因。

n=int(input())#人数
nums=[[0 for i in range(n+1)] for j in range(n+1)]#存储每个人之间的关系(是否认识)
m=int(input())
while m:
    a,b=map(int,input().split())
    nums[a][b]=nums[b][a]=1#如果a和b认识,赋值为1
    m-=1
rooms=[[1]]#先将第一个人开一个房间
for i in range(2,n+1):
    flag=0#标记第i个人是否能进入现有的房间
    for j in range(len(rooms)):遍历现有房间
        sum=0#统计次数
        for k in rooms[j]:
            sum+=1
            if nums[i][k]==1:#表示第i个人与这个房间里有认识的人
                break
            if sum==len(rooms[j]):#如果遍历完这个房间,都没认识的人,就把第i个人加入这个房间
                rooms[j].append(i)
                flag=1
                break
        if flag==1:
            break
    if flag==0:
        rooms.append([i])
print(len(rooms))

测试样例是正确的,但只有40%的通过率。原因在于假设现在有6个人,已经有5个人分为考场了

rooms=[[1],[2,3],[4,5]

现在6过来了,6跟1,3,4都认识,2跟第三个考场中4,5不认识,按照我的算法,将会为6开辟一个考场,实际上可以让2去第三个考场,6进第二个考场。

以下为正确做法

import os
import sys
# 输入节点个数n
n=int(input())
# 输入边的个数m
m=int(input())
# 创建一个n+1行n+1列的二维数组nums,用来存储图的邻接矩阵,nums[i][j]表示节点i和节点j之间是否有边相连,0表示没有,1表示有
nums=[[0]*(n+1) for i in range(n+1)]
# 创建一个n+1行n+1列的二维数组rooms,用来存储每个房间里放了哪些节点,rooms[i][j]表示第i个房间里的第j个节点,0表示空
rooms=[[0]*(n+1) for i in range(n+1)]
# 创建一个变量ans,用来记录最少需要的房间数,初始值设为一个很大的数
ans=1000000
# 循环m次,输入每条边的两个端点a和b,并更新nums数组
for i in range(m):
  a,b=map(int,input().split())
  nums[a][b]=1
  nums[b][a]=1
# 定义一个深度优先搜索函数DFS,参数x表示当前要放置的节点,cnt表示已经使用的房间数
def DFS(x,cnt):
  # 声明ans为全局变量,方便在函数内部修改
  global ans
  # 如果已经使用的房间数大于等于当前的最优解,就剪枝返回,不再搜索
  if cnt>=ans:
    return
  # 如果当前要放置的节点超过了n,说明所有节点都已经放置完毕,就更新最优解并返回
  if x>n:
    ans=min(cnt,ans)
    return
  # 循环从1到cnt+1,尝试把当前节点放在每个已经使用过或者新开的房间里
  for i in range(1,cnt+1):
    # 初始化一个变量k为0,用来记录当前房间里有多少个节点
    k=0
    # 循环判断当前房间里的每个节点是否和当前要放置的节点有边相连,如果没有就继续往后找,如果有就跳出循环
    while rooms[i][k]!=0 and nums[x][rooms[i][k]]==0:
      k+=1
    # 如果找到了当前房间里的一个空位,并且和当前要放置的节点没有边相连,就把当前节点放在这个空位上,并递归搜索下一个节点,然后把这个空位还原为0,并跳出循环(因为不需要再尝试其他房间了)
    if rooms[i][k]==0:
      rooms[i][k]=x
      DFS(x+1,cnt)
      rooms[i][k]=0
      break
  # 如果上面的循环没有找到合适的房间,就新开一个房间,并把当前节点放在这个房间里的第一个位置,并递归搜索下一个节点,然后把这个位置还原为0(因为可能有更优的方案)
  rooms[cnt+1][0]=x
  DFS(x+1,cnt+1)
  rooms[cnt+1][0]=0
# 调用DFS函数从第一个节点开始搜索,并传入初始房间数为0
DFS(1,0)
# 输出最优解ans
print(ans)
# 请在此输入您的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值