2015 年普及组初赛整理

本文介绍了如何解决排列问题,包括错排问题的两种解决方法,以及利用C++实现2015年月历打印的算法。此外,还涉及到二分查找法在求解奇数个整数中位数的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重新排列 1234 使得每一个数字都不在原来的位置上,一共有( 9 )种排法。

方法一:只有四个数,可以采用手工枚举的方式。

1234 2143 2413 3142 3412 3421 4132 4312 4321

因此答案为 9。

方法二:这是错排类问题。nn 个不同元素的一个错排可由下述两个步骤完成:

第一步,“错排” 11 号元素(将 11 号元素排在第 22 至第 nn 个位置之一 ),有 n-1n−1 种方法。

第二步,“错排”其余n−1 个元素,按如下顺序进行。视第一步的结果,若 1 号元素落在第 kk 个位置,第二步就先把 k 号元素“错排”好,kk 号元素的不同排法将导致两类不同的情况发生:1、k 号元素排在第 1 个位置,留下的 n−2 个元素在与它们的编号集相等的位罝集上“错排”,有 f(n -2)种方法;2、k 号元素不排第 1 个位置,这时可将第 1 个位置“看成”第 k个位置(也就是说本来准备放到 kk 位置为元素,可以放到 1 位置中),于是形成(包括 kk 号元素在内的)n−1 个元素的“错排”,有 f( n- l) 种方法。据加法原理,完成第二步共有 f(n-2)+f(n-1) 种方法。根据乘法原理,nn 个不同元素的错排种数 f(n)= (n-l)[f(n-2)+f(n-l)](n > 2)。

假设有 n 封信,第一封信可放在 (2~n) 任一个信封里,共 n−1 种放法,设第一封信放在了第 k 个信封里,若此时第 k 封信放在了第 1 个信封里,则只要将剩下的 n-2 错排,即 f(n-2),若第 k 封信没有放在了第 1个信封里,可将第 1 封信的位置看成是“第 k 个位置”,即将 n−1 封信错排,即为 f(n-l)。由递推可得:

f(n)=(n-1)\times[f(n-1)+f(n-2)]f(n)=(n−1)×[f(n−1)+f(n−2)]

f(1)=0,f(2)=1,f(3)=2,f(4)=9f(1)=0,f(2)=1,f(3)=2,f(4)=9。

(打印月历)输入月份 m(1 \le m \le 12)m(1≤m≤12),按一定格式打印 20152015 第 mm 月的月历。

例如,2015 年一月的月历打印效果如下(第一列为周日):

#include<iostream>
using namespace std;
const int dayNum[]={-1,31,28,31,30,31,30,31,31,30,31,30,31};
int m, offset, i;
int main()
{
    cin >> m;
    cout <<"S    M    T    W    T    F    S"<<endl;//'    '为tab制表符
    ①;
    for (i = 1; i < m; i++)
        offset = ②;
    for (i = 0; i < offset; i++)
        cout <<'    ';
    for (i = 1; i <= ③;i++)
    {
        cout << ④;
        if(i==dayNum[m]||⑤==0)
            cout << endl;
        else
            cout << '    ';
    }
    return 0;
}

填空位置 ①:

offset = 4

填空位置 ②:

(offset + dayNum[i]) % 7

填空位置 ③:

dayNum[m]

填空位置 ④:

i

填空位置 ⑤:

(offset + i) % 7

首先我们先看到较为简单的第三、四两空,这两空对应的过程显然就是输出日历了。for循环内应该是对每一天输出一个日期,因此循环范围就应该是这个月的天数,也就是dayNum[m]。什么时候应该输出换行符呢?要么就是所有日期输出完毕了(i==dayNum[m]),要么就是输出了一个星期之后进行换行,所以这里对应的就是每个星期结束,也就是当i为 77 的倍数时进行换行。下面再回过来看前两空。通过上面一个for循环可以看出来这个offset是用来控制月历开始的时候要输出多少的空位,再参照题目给出的第一个月的月历就可以知道offset初值应该是 44,即第一空对应需要做的事情。但是这个offset只是对于第一个月正确,因此需要再加上所有小于 11 月的月份的天数,计算offset的方法即为(offset+dayNum[i]) % 7

(中位数)给定 nn(nn 为奇数且小于 10001000)个整数,整数的范围在00~mm(0 \lt m \lt 2^{31}0<m<231)之间,请使用二分法求这 nn 个整数的中位数。所谓中位数,是指将这 nn 个数排序之后,排在正中间的数。

#include <iostream>
using namespace std;
const int MAXN = 1000;
int n,i,lbound,rbound,mid,m,count;
int x[MAXN];
int main()
{
    cin >> n >> m;
    for(i = 0; i < n; i++)
        cin >> x[i];
    lbound = 0;rbound = m;
    while(①) {
        mid=(lbound+rbound)/2;
        ②;
        for(i = 0; i < n; i++)
        {
            if(③)
                ④;
        }
        if(count > n/2)
            lbound = mid + 1;
        else
            ⑤;
    }
    cout << rbound << endl;
    return 0;
}

填空位置 ①:

lbound < rbound

填空位置 ②:

count = 0

填空位置 ③:

x[i] > mid

填空位置 ④:

count++

填空位置 ⑤:

rbound = mid

这是一道经典的左闭右闭的二分题,整个题目的过程为:先二分答案,再遍历整个数组验证。第一空是二分的条件,所以应该为lbound<rbound。第二空应该是将计数器count初始化操作,第三空、第四空就对应着统计大于(或小于)mid的数值个数的条件与过程,第五空则是调整右边界,可以参照左边界的调整方法仿写为rbound=mid

(01) 每个节点有零个或多个子节点;
(02) 没有父节点的节点称为根节点;
(03) 每一个非根节点有且只有一个父节点;
(04) 除了根节点外,每个子节点可以分为多个不相交的子树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值