趣味编程1

不知道下面描述的问题是否有个好听的名字,所以标题不知取什么的好。

问题描述:

根据上排给出十个数,在其下排填出对应的十个数:要求下排每个数都是先前上排那十个数在下排出现的次数。

举一个例子:
数值:0,1,2,3,4,5,6,7,8,9
分配:6,2,1,0,0,0,1,0,0,0

0在下排出现了6次,1在下排出现了2次,2在下排出现了1次,3在下排出现了0次....以此类推。

程序说明:

这个问题是在http://blog.csdn.net/v_july_v/article/details/5934051看到的,一时兴起编程实现了。

程序允许输入任意个任意整数(可以是负整数,数允许重复),输出所有可行解。对于例子中的0~9可以在很快的时间内给出解来。

程序的实现应该还有可以优化的地方,欢迎讨论交流。

程序源码:

/*



根据上排给出十个数,在其下排填出对应的十个数:要求下排每个数都是先前上排那十个数在下排出现的次数。

上排的十个数如下:

【0,1,2,3,4,5,6,7,8,9】

举一个例子:

数值:0,1,2,3,4,5,6,7,8,9

分配:6,2,1,0,0,0,1,0,0,0



0在下排出现了6次,1在下排出现了2次,

2在下排出现了1次,3在下排出现了0次....

以此类推。



*/



#include <iostream>

#include <vector>

#include <bitset>

#include <string>

#include <algorithm>



using namespace std;



#define MAXN	100 // 支持最大输入数个数



typedef struct tagNumFeq // 表示某个数字出现多少次的条件

{

	int num; // 数字

	int feq; // num的频率

}NumFeq;



int A[MAXN]; // 原始输入数

int B[MAXN]; // 一个可靠的输出



int ACount; // 实际输入数个数

bitset<MAXN> testPos; // 测试位标志,为1表示已为相应位试取值

vector<NumFeq> nfConds; // 条件集

int posibleResultCount(0); // 可行结果计数



// 数据输入

void Input()

{

	cout << "请输入数字个数:";

	cin >> ACount;

	if(ACount <= 0 || ACount > MAXN)

	{

		cout << "输入错误!" << endl;

		exit(-1);

	}



	cout << "请输入" << ACount << "个数:" << endl;

	for(int i = 0; i < ACount; i++)

	{

		cin >> A[i];

	}

	sort(A, A + ACount); // 对输入排序,方便处理

}



// 获得还有多少位没有测试

int GetUnTestCount()

{

	int re = ACount;

	int i = 0;

	while(testPos.test(i))

	{

		re--;

		i++;

	}

	return re;

}



// 显示一个可行的结果

void ShowResult()

{

	posibleResultCount++;



	cout << "----------可行结果" << posibleResultCount << "----------" << endl;



	cout << "a:";

	for(int i = 0; i < ACount; i++)

	{

		cout << "\t" << A[i];

	}

	cout << endl;



	cout << "b:";

	for(int i = 0; i < ACount; i++)

	{

		cout << "\t" << B[i];

	}

	cout << endl;



	cout << "------------end" << posibleResultCount << "------------" << endl;

}



// 获取所有条件所要求的未测试位数

int GetCondNumCount()

{

	int condNumCount; // 所有条件所要求的数的总和

	int i;



	// 先求出当前已测试位的情况

	condNumCount = 0;

	for(i = 0; i < ACount; i++)

	{

		if(testPos.test(i))

		{

			condNumCount += B[i];

		}

		else

		{

			break; // 排序了的,如果没有置位,则表示是未测试位

		}

	}

	

	// 减去已经满足的位

	for(int j = 0; j < i; j++)

	{

		for(int k = 0; k < i; k++)

		{

			if(B[k] == A[j])

			{

				condNumCount--;

			}

		}

	}



	return condNumCount;

}



// 返回是否所有条件都是零条件限制

bool IsAllZeroCond()

{

	for(int j = 0; j < nfConds.size(); j++)

	{

		if(nfConds[j].feq != 0)

		{

			return false;

		}

	}

	return true;

}



// 查找是否有关于num的条件,有的话,把相应条件所要求的feq减1。如果该条件所要求的feq为0,则置zeroRequested为真(其默认为假)

// 返回值表示是否有关于num的条件

bool MMIfNum(int num, bool &zeroRequested)

{

	zeroRequested = false;

	for(int i = 0; i < nfConds.size(); i++)

	{

		if(nfConds[i].num == num)

		{

			if(nfConds[i].feq > 0)

			{

				nfConds[i].feq--;

			}

			else if(nfConds[i].feq == 0)

			{

				zeroRequested = true;

			}

			else

			{

				// 本分支理论上不会有的

				cout << "程序数据异常……。" << endl;

			}

			return true;

		}

	}

	return false;

}



// 恢复因调用MMIfNum所产生的影响,hasCond是调用MMIfNum时的返回值,num是传入的参数,isZeroRequested是调用后函数对它第二个参数的设置值。

// 返回值表示是否恢复了影响

bool PPmm(bool hasCond, int num, bool isZeroRequested)

{

	if(hasCond && !isZeroRequested)

	{

		for(int i = 0; i < nfConds.size(); i++)

		{

			if(nfConds[i].num == num)

			{

				nfConds[i].feq++;

				return true;

			}

		}

		return false;

	}

	return true;

}



// 还原设置某频率为num进而对num数频率的影响

// 返回值表示是否还原了影响

bool PPSetPos(int num)

{

	for(int i = 0; i < nfConds.size(); i++)

	{

		if(nfConds[i].num == num)

		{

			nfConds[i].feq++;

			return true;

		}

	}

	return false;

}



// 修正添加条件前条件的默认值,处理的情况是前面已经出现过条件中所示的num值了。nf的feq域应保证能满足已经出现的num个数

void JustfyCondWhenAdd(NumFeq & nf)

{

	for(int i = 0; i < ACount; i++)

	{

		if(testPos.test(i))

		{

			if(B[i] == nf.num)

			{

				nf.feq--; // 这里可能减到负值

			}

		}

		else

		{

			break;

		}

	}

}



// 获得已经设置的位中频度为num的个数

int GetSetedNumFeq(int num)

{

	int re = 0;

	for(int i = 0; i < ACount; i++)

	{

		if(testPos.test(i))

		{

			if(B[i] == num)

			{

				re++;

			}

		}

		else

		{

			break;

		}

	}

	return re;

}



// 打印当前数据状态,VS中调试用

string PA()

{

	char buf[1024];

	char *bp = buf;

	int cnt;



	cnt = sprintf(bp, "t");bp += cnt;

	for(int i = 0; i < ACount; i++)

	{

		cnt = sprintf(bp, "\t%d", testPos.test(i));bp += cnt;

	}

	cnt = sprintf(bp, "\n");bp += cnt;



	cnt = sprintf(bp, "A");bp += cnt;

	for(int i = 0; i < ACount; i++)

	{

		cnt = sprintf(bp, "\t%d", A[i]);bp += cnt;

	}

	cnt = sprintf(bp, "\n");bp += cnt;



	cnt = sprintf(bp, "B");bp += cnt;

	for(int i = 0; i < ACount; i++)

	{

		cnt = sprintf(bp, "\t%d", B[i]);bp += cnt;

	}

	cnt = sprintf(bp, "\n");bp += cnt;



	cnt = sprintf(bp, "d");bp += cnt;

	for(int i = 0; i < nfConds.size(); i++)

	{

		cnt = sprintf(bp, "\t(%d,%d)", nfConds[i].num, nfConds[i].feq);bp += cnt;

	}

	cnt = sprintf(bp, "\n\n");bp += cnt;



	return string(buf);

}



// 添加条件,纯调用push_back,只是为了方便测试用。

void AddCondition(NumFeq nf)

{

	nfConds.push_back(nf);

	//printf("ad (%d,%d)\n", nf.num, nf.feq);

	//if(nf.num == 9 && nf.feq == 0)

	//{

	//	printf("a");

	//}

	//printf(PA().c_str());

}



// 删除条件,纯调用pop_back,只是为了方便测试用。

void DelCondition()

{

	//NumFeq nf = nfConds[nfConds.size() - 1];

	//printf("dd (%d,%d)\n", nf.num, nf.feq);

	nfConds.pop_back();

	//printf(PA().c_str());

}



// 获得前面条件所要求必须要有的数

void GetLimitedValues(vector<int> & cont)

{

	cont.clear();

	int num, feq;

	for(int i = 0; i < nfConds.size(); i++)

	{

		feq = nfConds[i].feq;

		num = nfConds[i].num;

		for(int j = 0; j < feq; j++)

		{

			cont.push_back(num);

		}

	}

}



// 获得前面条件中所要求不能出现的数

void GetForbidValues(vector<int> & cont)

{

	cont.clear();

	for(int i = 0; i < nfConds.size(); i++)

	{

		if(nfConds[i].feq == 0)

		{

			cont.push_back(nfConds[i].num);

		}

	}

}



// 获取已设置的值所占用的位数

int GetUsedPosCount()

{

	int re = 0;

	int i;

	for(i = 0; i < ACount - 1; i++)

	{

		if(testPos.test(i))

		{

			re += B[i];

		}

		else

		{

			break;

		}



		if(testPos.test(i + 1) && A[i] == A[i + 1]) // 这里有重复值,直接跳过re+=操作

		{

			i++;

		}

	}

	if(i == ACount - 1 && testPos.test(i)) // 最后两项不是相同的

	{

		re += B[i];

	}

	// 从这里要求所有的都要排序,并且穷举是从A[0]到A[ACount-1]逐一开始的



	return re;

}



// 已测试的A中,是否测试过当前A[pos],即aValue,如果有,本变量代表其在A数组中的索引

int FindAPosValueIndex(int aValue)

{

	for(int i = 0; i < ACount; i++)

	{

		if(testPos.test(i))

		{

			if(A[i] == aValue)

			{

				return i;

			}

		}

		else

		{

			break;

		}

	}

	return -1;

}



// 递归开始测试各位的取值情况

void TestPos(int pos)

{

	int condNumCount = GetCondNumCount();

	int unTestCount = GetUnTestCount();

	if(condNumCount > unTestCount) // 如果本轮刚开始的时候,就发现所要求的位数大于未测试的位数,则不用说,测试失败。

	{

		return;

	}



	int lastAPosValueIndex = FindAPosValueIndex(A[pos]);



	if(lastAPosValueIndex >= 0) // 前面有条件限制,则与前面的保持一致,不用逐一测试

	{

		// 设置当前位为相应值

		B[pos] = B[lastAPosValueIndex];

		// 在设置当前位后,看当前的条件是否满足,满足的话,递归测试其他处理

		// 这里的影响是,看是否有对num为B[lastAPosValueIndex]的条件,有的话,更改条件。

		bool zr1, hasCond;

		hasCond = MMIfNum(B[pos], zr1); // 这里的调用!!不一定!!返回为真

		if(hasCond && zr1) // 有这样的条件,并且要求不能出来B[pos],则当前设置B[pos]就不符合要求了,本次测试应返回失败

		{

			PPmm(hasCond, B[pos], zr1); // 恢复影响,本调用与MMIfNum调用相对应

			// 注意,这里没有设置testPos相应标志位

			return;

		}

		else

		{

			// 要么没有关于B[pos]的条件,要么有条件并且已经把条件弱化了1,假如存在弱化1,则是必要的,不需要调用PPmm恢复影响

			// 到这里,在本调用开始就测试过,所要求的位数符合要求

			testPos.set(pos); // 所有测试条件满足,置测试位表示通过

			// 这里可以开始递归测试了

			int np = (pos >= ACount - 1) ? -1 : (pos + 1);

			if(np == -1) // 测试到这里,所有位都顺利通过了

			{

				if(IsAllZeroCond())

				{

					ShowResult(); // 这里在开头知有足够的位满足条件,而这里找下一个未测试位又说没有,则说明条件全是零条件限制。直接显示结果。

				}

			}

			else

			{

				// 这里的情况是,还有其他位没有测试,则选择下一个待测试位继续(在下一轮中可能有若干循环,都无所谓了,它们不应该找到一种可行结果就返回,所以这里可以调用return语句)

				TestPos(np);

			}

			testPos.set(pos, false); // 还原

		}

	}

	else // 要没前面没有A[pos]的条件限制,则循环测试可行的取值

	{

		// 先计算三类条件限制



		// 1 有取值限制,取何值

		bool valueLimited = (unTestCount == condNumCount); // 未测试位必须全部用于满足前面的条件要求

		vector<int> limitedValues;

		if(valueLimited)

		{

			GetLimitedValues(limitedValues);

		}



		// 2 不能取何值

		vector<int> forbidValues;

		GetForbidValues(forbidValues); // 同时获得不准出现的取值



		// 3 最大可能的取值

		int maxValue = ACount - GetUsedPosCount(); // 同时限定最大取值



		for(int i = 0; i <= ACount; i++) // 注意,这里是可以取ACount值的,比如只有一个1,

		{

			if(i > maxValue)

			{

				break; // 直接退出循环。

			}



			if(valueLimited // 这里取值的过滤还是有一些作用啦,只是还有等更多优化

				&& (find(limitedValues.begin(), limitedValues.end(), i) == limitedValues.end()))

			{

				continue; // 当前值可以直观地看出不符合要求。

			}

			if(find(forbidValues.begin(), forbidValues.end(), i) != forbidValues.end()) // 找到不准出现的,就是零次取值条件要求的。

			{

				continue;

			}



			B[pos] = i; // 对应一个新的条件:A[pos]得出现i次

			// 添加一个新的条件(A[pos], B[pos])

			int naFeq = GetSetedNumFeq(A[pos]);

			if(naFeq > i) // 当前A[pos]的频度不得少于naFeq,所以小于等于naFeq的i的取值是不可取的。

				// 另外为等于的情况是说A[pos] == B[pos] == naFe,所以这样的情况是不可以的

			{

				continue;

			}

			if(A[pos] == i && naFeq == i/* && i != 0*/) // 这里要考虑剔除为0的情况吗?

			{

				continue;

			}



			// 添加一个新条件

			NumFeq nf;

			nf.num = A[pos];

			nf.feq = i; // 也即是当前的B[pos]

			JustfyCondWhenAdd(nf); // 修正影响,如前面有出现过

			AddCondition(nf);



			// 验证新条件是否满足当前情况



			// 检测当前取值是否满足继续向里测试的条件

			bool zr1, hasCond;

			hasCond = MMIfNum(B[pos], zr1); // 这里的调用!!不一定!!返回为真

			if(hasCond && zr1) // 有这样的条件,并且要求不能出来B[pos],则当前设置B[pos]就不符合要求了,本次测试应返回失败

			{

				// 需要考虑的清理工作是删除新加的条件

				DelCondition();

				continue;// 继续其他值的测试 考虑删除本行

			}

			else

			{

				// 要么没有关于B[pos]的条件,要么有条件并且已经把条件弱化了1

				// 那么这里只需要关心在没有B[pos]的条件下,需要添加一个B[pos]条件。

				// 到这里,在本调用开始就测试过,所要求的位数符合要求

				testPos.set(pos); // 所有测试条件满足,置测试位表示通过

				// 这里可以开始递归测试了

				int np = (pos >= ACount - 1) ? -1 : (pos + 1);

				if(np == -1) // 测试到这里,所有位都顺利通过了

				{

					if(IsAllZeroCond())

					{

						ShowResult();

					}

				}

				else

				{

					// 这里的情况是,还有其他位没有测试,则选择下一个待测试位继续(在下一轮中可能有若干循环,都无所谓了,它们不应该找到一种可行结果就返回,所以这里可以调用return语句)

					TestPos(np);

				}



				DelCondition();

				testPos.set(pos, false);

				PPSetPos(B[pos]); // 还原影响				

			}

		}

	}

}



int main(int argc, char **argv)

{

	Input();

	TestPos(0);

	return 0;

}

你可能感兴趣的:(编程)