趣味编程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;
}
浙公网安备 33010602011771号