1. 程式人生 > >每日一演算法:m元素集合的n個元素子集

每日一演算法:m元素集合的n個元素子集

說明

假設有個集合擁有m個元素,任意的從集合中取出n個元素,則這n個元素所形成的可能子集有那些?

解法

假設有5個元素的集點,取出3個元素的可能子集如下:

{1 2 3}、{12 4 }、{12 5}、{13 4}、{13 5}、{14 5}、{23 4}、{23 5}、{24 5}、{34 5}

這些子集已經使用字典順序排列,如此才可以觀察出一些規則:

如果最右一個元素小於m,則如同碼錶一樣的不斷加1

如果右邊一位已至最大值,則加1的位置往左移

每次加1的位置往左移後,必須重新調整右邊的元素為遞減順序

所以關鍵點就在於哪一個位置必須進行加1的動作,到底是最右一個位置要加1?還是其它的位置?

在實際撰寫程式時,可以使用一個變數positon來記錄加1的位置,position的初值設定為n-1,因為我們要使用陣列,而最右邊的索引值為最大 的n-1,在position位置的值若小於m就不斷加1,如果大於m了,position就減1,也就是往左移一個位置;由於位置左移後,右邊的元素會 經過調整,所以我們必須檢查最右邊的元素是否小於m,如果是,則position調整回n-1,如果不是,則positon維持不變。

#include <stdio.h>

#define MAX  20

int main()
{
	int set[MAX];
	int m,n,position;
	int i;
	printf("輸入集合個數:");
	scanf("%d",&m);
	printf("輸入取出元素個數:");
	scanf("%d",&n);
	for (i=0; i<n; i++)
	{//設定第一個集合
		set[i] = i+1;
	}
	for (i=0; i<n; i++)
	{//列印第一個集合
		printf("%d ",set[i]);
	}
	printf("\n");
	position = n-1;//設定當前位置指向第一個集合的最後一個元素
	while (1)
	{
		if (set[n-1] == m)
		{//如果最後一個元素已經是最大元素,則位置前移,指向前一個元素
			position--;
		}
		else
		{//否則位置指向最後一個元素
			position = n-1;
		}
		set[position]++;//當前位置值加1
		for (i=position+1; i<n; i++)
		{//後面的值逐個處理,即比前一個元素大1
			set[i] = set[i-1]+1;
		}
		for (i=0; i<n; i++)
		{//顯示集合
			printf("%d ",set[i]);
		}
		printf("\n");
		if (set[0] >= m-n+1)
		{//已經處理完畢
			break;
		}
	}
	return 0;
}