这一章的题目讲的是归纳法,采用到的例子有:选择排序,插入排序,基数排序,整数幂,多项式求值,生成排列,多数元素(见课本)。
归纳推理定义:归纳推理是一种由个别到一般的推理。由一定程度的关于个别事物的观点过渡到范围较大的观点,由特殊具体的事例推导出一般原理、原则的解释方法。
归纳一般可以化为包括所有递归算法设计技术,如:分治法和动态规划,而这章中的所有题目和算法,都采用的尾递归。
1.全排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
2.字典序:按字母顺序排列的方法 。
3.采用递归的方法:将未标记的数字填入存放数据的数组num的第step位,并将填过的数据标记,然后递归往后面走(step+1位),直到num的前N位全部递归过,然后就输出返回。
该方法时间复杂度为O(n!),空间复杂度为Θ(n)
#include
using namespace std;
int num[10],sign[10],N;
void ergodic(int step)
{
if (step == N+1)
{
for(int tmp = 1; tmp <= N; tmp++)
cout << num[tmp];
cout << '\n';
return;
}
for (int tmp = 1; tmp <= N; tmp++)
{
if (sign[tmp] == 0)
{
num[step] = tmp;
sign[tmp] = 1;
ergodic(step + 1);
sign[tmp]=0;
}
}
return;
}
int main()
{
cin >> N;
ergodic(1);
return 0;
}
1.排序:和上题目一样的分析方法(但是这次是逆序)。
2.只要前t项:设置一个全局变量进行标记,每输出一次就该变量-1,当为0时,终止所有递归。
3:每一项都要比前面小,就每次遍历的值传进去,从该值循环往后遍历,因为题目给出(1<=t<=C(n,r)),所以不用考虑是否组数不够的问题。
该代码时间复杂度为O(n!),空间复杂度为Θ(n)
#include
using namespace std;
int answer[30],N,R,T;
void ergodic(int step, int step2)
{
if(T == 0)
return;
if (step == R)
{
for(int tmp = 0; tmp < R; tmp++)
cout << " " << answer[tmp];
cout < 0; tmp--)
{
answer[step] = tmp;
ergodic(step+1, tmp-1);
}
return;
}
int main()
{
cin >> N;
cin >> R;
cin >> T;
ergodic(0, N);
return 0;
}
个人觉得“每一个组合结果中的值从大到小排列"这段话不够严谨,应该改成“结果内的值从大到小排列”,如果按照每一组合之间从大到小排列,运行结果就应该是
的输出结果。
顺带附上我的错误理解的代码(打了就不能浪费)。
#include
using namespace std;
int sign[30],answer[30],N,R,T;
void ergodic(int step)
{
if(T == 0)
return;
if (step == R)
{
for(int tmp = 0; tmp < R-1; tmp++)
cout << answer[tmp] << " ";
if(T == 1)
cout << answer[R-1];
else
cout << answer[R-1] < 0; tmp--)
{
if (sign[tmp] == 0)
{
answer[step] = tmp;
sign[tmp] = 1;
ergodic(step+1);
sign[tmp]=0;
}
}
return;
}
int main()
{
cin >> N;
cin >> R;
cin >> T;
ergodic(0);
return 0;
}
这个不是可ac代码。
这个不是可ac代码。。
这个不是可ac代码。。。。。。
1.将缺的数字一个个填入空缺中,然后找出有多少组顺序对(将未标记的数字填入存放数据的数组num的第step位,并将填过的数据标记,然后递归往后面走(step+1位),直到num的前N位全部递归过,然后看有多少组顺序对,如果与K值相等,则有一种排列方式)
2.这样的话时间复杂度为O(n!n2)(一次长度为N的循环,n次递归),空间复杂度为Θ(n2),时间需要优化一下,可以发现在那么多次递归到最后的比较,有很多次的比较是重复的,所以我们可以把之前知道的部分先进行对比(这段在所有比较中都有),这样时间复杂度就能减为O(n!na)。a为缺失的数组数量。
#include
#include
using namespace std;
//num用于记录数据,sign用于标记缺失数字(从1开始记录),vacancy_side用于记录缺失的位置
//vacancy_num用于记录缺失的数字,sign2用于记录数字是否用过,相当于上题的sign
int n, k, num[100], sign[101], vacancy_side[10], vacancy_num[10], sign2[10];
int vacancy_length = 0 , answer = 0, all = 0;
void ergodic(int step)
{
if(step == vacancy_length)//当步数等于缺失的数字时,说明所有空缺都填满了
{
if(all == k)//如果顺序对的数量和k相等,则说明这种排序可以
{
answer++;
}
return;
}
for(int tmp = 0; tmp < vacancy_length; tmp++)
{
int big_num = 0;
if(sign2[tmp] == 0)
{
num[vacancy_side[step]] = vacancy_num[tmp];
if(vacancy_side[step] != 0)
for(int tmp2 = 0; tmp2 < vacancy_side[step]; tmp2++)//往前找多少比他大的
if(vacancy_num[tmp] > num[tmp2])
big_num++;
for(int tmp2 = vacancy_side[step] + 1; tmp2 < n; tmp2++)//往后找多少比他小的
if(vacancy_num[tmp] < num[tmp2])
big_num++;
all += big_num;//插入该点增加的顺序对
sign2[tmp] = 1;//给这个数字做标记
ergodic(step+1);
all -= big_num;
num[vacancy_side[step]] = 0;
sign2[tmp] = 0;
}
}
}
int main()
{
memset(num,0,sizeof(int)*100);
memset(sign,0,sizeof(int)*101);
memset(sign2,0,sizeof(int)*10);
memset(vacancy_side,0,sizeof(int)*10);
memset(vacancy_num,0,sizeof(int)*10);
cin >> n;
cin >> k;
for(int tmp = 0; tmp < n; tmp++)
{
cin >> num[tmp];
sign[num[tmp]]++;
if(num[tmp] == 0)//找出空缺的位置
{
vacancy_side[vacancy_length] = tmp;
vacancy_length++;
}
}
vacancy_length = 0;
for(int tmp = 1; tmp <= n; tmp++)//找出空缺的数值
{
if(sign[tmp] == 0)
{
vacancy_num[vacancy_length] = tmp;
vacancy_length++;
}
}
for(int tmp = 1; tmp < n; tmp++)//找出重复的部分
for(int tmp2 = 0; tmp2 < tmp; tmp2++)
if(num[tmp] > num[tmp2] && num[tmp2] != 0)
k--;
ergodic(0);
cout << answer <
1.行列式的算法:这里采用余因子展开式,detA = ai1Ci1 + ai2Ci2 + … ++ ainCin,Cij = (-1)i+jdetAij,n的矩阵行列式可以拆分成,n个n-1行矩阵行列式乘上(-1)i+j的和。
2.与上面一样采用递归的想法,标记已经使用过的行,递归至第n列。
#include
using namespace std;
int num, data[10][10], sign[10];
int ergodic(int step)
{
int answer = 0, num2 = 1;//num2´¢´æ·ûºÅ
if (step == num-1)
{
for(int tmp = 0; tmp < num; tmp++)
if(sign[tmp] == 0)
return data[tmp][step];
}
for (int tmp = 0; tmp < num; tmp++)
{
if (sign[tmp] == 0)
{
sign[tmp] = 1;
answer += (num2 * data[tmp][step] * ergodic(step+1));
sign[tmp] = 0;
num2 *= -1;
}
}
return answer;
}
int main()
{
cin >> num;
for(int tmp = 0; tmp < num; tmp++)
for(int tmp2 = 0; tmp2 < num; tmp2++)
cin >> data[tmp][tmp2];
cout << ergodic(0) <
这章只是简单的归纳法入门,为后面细讲动态规划和分治法打基础,重点把握由大问题化成若干小问题来算。