归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算

章节大概

这一章的题目讲的是归纳法,采用到的例子有:选择排序,插入排序,基数排序,整数幂,多项式求值,生成排列,多数元素(见课本)。
归纳推理定义:归纳推理是一种由个别到一般的推理。由一定程度的关于个别事物的观点过渡到范围较大的观点,由特殊具体的事例推导出一般原理、原则的解释方法。
归纳一般可以化为包括所有递归算法设计技术,如:分治法和动态规划,而这章中的所有题目和算法,都采用的尾递归。

题目(1)

归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算_第1张图片

简单分析

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;
}

题目(2)

归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算_第2张图片这题题目表述个人觉得有歧义,后面备注详细说明一下。

简单分析

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;
}

备注:

个人觉得“每一个组合结果中的值从大到小排列"这段话不够严谨,应该改成“结果内的值从大到小排列”,如果按照每一组合之间从大到小排列,运行结果就应该是归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算_第3张图片
的输出结果。
顺带附上我的错误理解的代码(打了就不能浪费)。

#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代码。。。。。。

题目(3)归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算_第4张图片

简单分析

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 <

题目(4)归纳法 输出全排列 前t个组合结果 排列还原 行列式的计算_第5张图片

分析

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) <

总结

这章只是简单的归纳法入门,为后面细讲动态规划和分治法打基础,重点把握由大问题化成若干小问题来算。

你可能感兴趣的:(C++,归纳法,排列,行列式计算)