題目請前往以下連結查看,由於該練習題在網上被大量轉載,原出處似乎很難找到,從中挑選到一時間較早的,如下:
資訊學 (電腦) 奧林匹克訓練題(中級部分)
網上能搜到一些答案,由於時間限制,本人暫不一一列出。
今天做了第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;}