题目请前往以下链接查看,由于该练习题在网上被大量转载,原出处似乎很难找到,从中挑选到一时间较早的,如下:
信息学 (计算机) 奥林匹克训练题(中级部分)
网上能搜到一些答案,由于时间限制,本人暂不一一列出。
今天做了第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;
}