全排列除法

題目

Time Limit: 3000ms ,Memory Limit: 10000KB,Accepted: 5030 ,Total Submissions: 9489

Description
輸入正整數n,按從小到大的順序輸出所有形如abcde/fghij=n的表達式,其中a~ j恰好爲數字0 ~ 9的一個排列, 2≤n<=79。

Input
輸入正整數n

Output
輸出形如abcde/fghij=n的表達式,每行一個

Sample Input
62
Sample Output
79546/01283=62
94736/01528=62

思路:

全排列的時間複雜度太高,所以得在中間剪枝。
我們可以由n得到最小的那個abcde,即最小的fghij,按照數學裏的叫法,把它們叫做被除數和除數。
全排列的經典解法就是讓首位分別與後面數字交換,並遞歸求解後面的那些排列。
可以發現,當我們遞歸到某個位置時,再對子問題去執行排列操作已經沒有意義了
在n固定的前提下,除數越大則被除數越大,我們可以找到最小的5位除數,從而得到最小的可能被除數(不一定滿足數字唯一)去做剪枝
若得到的最小可能被除數位數大於5則找不到。否則在查找過程中,若剛好在第五位的時候,就判斷一下後面五位是否有機會排列得到結果
#include<stdio.h>
#include<time.h>
#define N 11
#define LENGTH 5
//函數聲明
int GetNumLength(int num);
void TreaverStr(char *str, int l, int r, int preNum, int n, int *numArray, int *len);
int GetNumForStr(char *str, int l, int r);
void SwapChar(char *str, int l, int r);
int IsValid(int bigNum, int smallNum, int n);


int main(void)
{
	int n, preNum = 0, len = 0, tempNum = 0;
	int numArray[30] = { 0 };
	char info[N] = "9876543210";
	clock_t start, stop;
	scanf("%d", &n);
	start = clock();
	//已知 五位數/五位數=n
	//用最小的五位數乘以n,得到最大可能的被除數,通過這個去剪枝
	preNum = 1234 * n;
	if (GetNumLength(preNum) > LENGTH)//Error
		return -1;
	TreaverStr(info, 0, N - 2, preNum, n, numArray, &len);

	//sort
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = i + 1; j < len; j++)
		{
			if (numArray[i] > numArray[j])
			{
				tempNum = numArray[i];
				numArray[i] = numArray[j];
				numArray[j] = tempNum;
			}
		}
	}
	for (int i = 0; i < len; i++)
	{
		printf("%d/%d=%d\n", numArray[i] * n, numArray[i], n);
	}
	stop = clock();
	printf("用時:%lf\n", (double)(stop - start) / CLK_TCK);
	getchar();
	getchar();
	getchar();
	return 0;
}

void TreaverStr(char *str, int l, int r, int preNum, int n, int *numArray, int *len)
{
	int flag = 1, pN = 0, lN = 0;
	if (l >= r)//結束
	{
		pN = GetNumForStr(str, 0, LENGTH - 1);
		lN = GetNumForStr(str, LENGTH, r);
		//判斷是否是需要的數
		if (!IsValid(pN, lN, n))
			return;
		numArray[*len] = lN;
		*len = *len + 1;
		return;
		/*for (int i = 0; i <= r; i++)
		{
			if (i == LENGTH)
				putchar('/');
			putchar(str[i]);
		}
		printf("=%d\n",n);*/
	}
	for (int i = l; i <= r; i++)
	{
		flag = 1;
		SwapChar(str, l, i);
		//判斷是否需要繼續算下去
		//剛好達到前綴項那麼長,看是否可以剪枝,不行的話後面五位就不用算了
		if (i == LENGTH - 1 && GetNumForStr(str, 0, i) < preNum)
			flag = 0;
		if (1 == flag)
			TreaverStr(str, l + 1, r, preNum, n, numArray, len);
		SwapChar(str, l, i);//換回來
	}

}

int IsValid(int bigNum, int smallNum, int n)
{
	return smallNum * n == bigNum ? 1 : 0;
}
void SwapChar(char *str, int l, int r)
{
	char ch = str[l];
	str[l] = str[r];
	str[r] = ch;
}
int GetNumForStr(char *str, int l, int r)
{
	int num = 0;
	while (l <= r)
		num = num * 10 + str[l++] - '0';
	return num;
}

//獲取num的長度
int GetNumLength(int num)
{
	int len = 1;
	if (num < 0)
		return num *= -1;
	while ((num /= 10))
	{
		++len;
	}
	return len;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章