信息学 (计算机) 奥林匹克训练题(中级部分)

这篇博客分享了信息学计算机奥林匹克的中级训练题目,虽然原出处难以追溯,但提供了较早的参考资料链接。作者提到自己完成了第30题,并感觉有一定难度,提到了相关分析可以参考2_组合计数的资料。文中还分享了个人的解题思路,但表示未经优化,可能存在逻辑问题,目前在Code::Blocks环境下通过了调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目请前往以下链接查看,由于该练习题在网上被大量转载,原出处似乎很难找到,从中挑选到一时间较早的,如下:

信息学 (计算机) 奥林匹克训练题(中级部分)

网上能搜到一些答案,由于时间限制,本人暂不一一列出。

今天做了第30道,觉得有点难度。

某机要部门安装了电子锁。M个工作人员每人发一张磁卡,卡上有开锁的密码特征。

 

为了确保安全,规定至少要有N个人同时使用各自的磁卡才能将锁打开。问电子锁上至 少要有多少种特征?每个人的磁卡上至少要有多少特征?如果特征的编号以小写英文字母表示,将每个人的磁卡的特征编号打印出来,要求输出的电子锁的总特征数最少。

 

设 3<=M<=7, 1<=N<=4, M与N由键盘输入,工作人员编号用1#,2#,...表示.

相关分析可以看2_

上面的讲解很清晰 (百度文库 0积分下载)

我的解法如下:(写了一上午,头晕死了,还没优化过,可能有部分逻辑不清的地方,暂时输出的结果是对的,Code::Blocks下调试通过)

#include <stdio.h>
char table[35][7] = {0};
char pers[7][35] = {0};
int M;
int N;
int factorial(int integer)
{
    int ret = 1;
    if (!integer)
    {
        return 1;
    }
    while (integer)
    {
        ret *= integer--;
    }
    return ret;
}
int Combination(int m, int n)
{
    return factorial(m)/(factorial(n)*factorial(m-n));
}
void init_combination(int n)
{
    int i;
    for (i = 0; i<n; i++)
    {
    	table[0][i] = 1;
    }
}
void next_combination(int num, int n)
{
    int i;
    int len = 0;
    int stop = 0;
    for (i = 0; i < num; i++)
    {
    	if (table[n][i] == 0)//当前为0
    	{
    	    if (stop == 0) len++;
    	}
    	else if (len > 0)//当前为1 len>0
    	{
    	    if (stop == 0)
    	    {
    	       if(table[n][i + 1] == 1)//下一项为1
                {
                    table[n + 1][i - len] = 1;
                    table[n + 1][i] = 0;
                }
                else//下一项为0
                {
                    table[n + 1][i] = 0;
                    table[n + 1][i + 1] = 1;
                    stop = 1;
                }
    	    }
    	    else
    	    {
    	        table[n + 1][i] = 1;
    	    }


    	}
    	else//当前为1 len=0
    	{
    	    if(table[n][i + 1] == 0 && stop == 0)//下一项为0
    	    {
    	        table[n + 1][i] = 0;
    	        table[n + 1][i + 1] = 1;
    	        stop = 1;
    	    }
    	    else//下一项为1
    	    {
    	        table[n + 1][i] = 1;
    	    }

    	}
    }
}
void set_person_keys()
{
    int i, j;
    for (i = 0; i < Combination(M,M - N + 1); i++)
    {
    	for (j = 0; j < M; j++)
    	{
    		if (table[i][j] == 1)
    		{
    			pers[j][i]='A' + i;
    		}
    	}
    }
}
int main(void)
{
    int i, j;
    int key,person;
    printf("某机要部门安装了电子锁。M个工作人员每人发一张磁卡,卡上有开锁的密码特征。\n请输入总人数M(3<=M<=7):\n");
    scanf("%d", &M);
    printf("为了确保安全,规定至少要有N个人同时使用各自的磁卡才能将锁打开。\n请输入N的值(1<=N<=4):\n");
    scanf("%d", &N);
    key = Combination(M,N-1);
    printf("电子锁上至少要有%d种特征\n", key);
    person = Combination(M-1, N-1);
    printf("每个人的磁卡上至少要有%d种特征\n", person);

    init_combination(M - N + 1);
    for (i = 0; i < Combination(M,M - N + 1); i++)
    {
        /*清除下面5行的“//”可显示从M人中挑选(M-N+1)人的所有组合(用二进制位表示)*/
//    	for (j = 0; j < M; j++)
//    	{
//    		printf("%d", table[i][j]);
//    	}
//    	printf("\n");
    	next_combination(M, i);
    }
    set_person_keys();
    for (i = 0; i < M; i++)
    {
        printf("%d# ", i+1);
    	for (j = 0; j < Combination(M,M - N + 1); j++)
    	{
    		printf("%c",pers[i][j]);
    	}
    	printf("\n");
    }
    printf("上面每一列字母的个数都是%d,空余数为%d,对于每一列来说任选%d行都可以选择到一个字母\n",M-N+1,N-1,N);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值