2010NOIP普及组初赛 难题总结与延伸(队列、初等数论、递归搜索)

这套题有一点难度,不过没关系,只要你认真学习、全力思考,困难将会在笔墨之间,消散。


一.代数

题面 

7.设X、Y、Z分别代表三进制下的一位数字,若等式XY+ZX=XYX在三进制下成立,那么同样在三进制下,等式XY∗ZX=()也成立。

  • A   YXZ

  • B  ZXY

  • C   XYZ

  • D   XZY

答案解析 

我们分析一下就可以了:

1.先看第一位,不可能发生进位,又X+Y=X。所以,Y就等于0;

2.再看第二位,X+Z=XY。因为Y=0,所以X+Z=X0。

2.1 我们可以假设X=1,Z=2(三进制,X不等于Y不等于Z),发现 1+2=10,成立;

2.2 又设X=1,Z=2,发现2+1=10≠20,因此不成立。

因此,X=1,Z=2;

综上所述,X=1,Y=0,Z=2,

所以XY*ZX=10*21=210=YXZ

故选B。


二.进制转换

题面

13.一个自然数在十进制下有n位,则它在二进制下的位数与( )最接近。

  • A  5n

  • B  n * log2(10)

  • C  10 * log2(n)

  • D  10^n * log2(n)

 答案解析

十进制转二进制一般使用除二取余法

可以举例尝试

①两位数37(2位),转换成二进制是100101(6位)

A项:5*2=10(位)

B:项 n*log 210≈6.64(位)

C项: 10*log 2n=10(位)

D项:10 nlog 2n =100(位)

②三位数176(3位),转换成二进制是10110000(8位)

A项:5*3=15(位)

B:项:n*log 210≈10.53(位)

C项: 10*log 2n≈15.8(位)

D项:10 nlog 2n =1584.96(位)

没有好的证明方法,通过举例可以看出,B项是最接近的。


三.双向链表

题面

双向链表中有两个指针域llink和rlink,分别指向该结点的前驱及后继。设p指向链表中的一个结点,它的左右结点均非空。现要求删除结点p,则下面语句序列中错误的是( )。
A. p->rlink->llink = p->rlink;
p->llink->rlink = p->llink; delete p;
B. p->llink->rlink = p->rlink;
p->rlink->llink = p->llink; delete p;
C. p->rlink->llink = p->llink;
p->rlink->llink->rlink = p->rlink; delete p;
D. p->llink->rlink = p->rlink;
p->llink->rlink->llink = p->llink; delete p;

答案解析 

看似很复杂,实际上只要学习一下链表相关知识就行了。

排除法做,就行了。

A的第一行的结果是使得结点p的后继结点的前驱指向了它本身,逻辑错误。

故选A。

另:我以前写过一篇有关链表的文章,不会链表的建议看一下:【数据结构】链表 详解


四.队列

题面

队列快照是指在某一时刻队列中的元素组成的有序序列。例如,当元素1、2、3入队,元素1出队后,此刻的队列快照是"2 3"。当元素2、3也出队后,队列快照是"",即为空。现有3个正整数元素依次入队、出队。已知它们的和为8,则共有_________种可能的不同的队列快照(不同队列的相同快照只计一次)。例如,"5 2 1"、"4 2 2"、""都是可能的队列快照;而"7"不是可能的队列快照,因为剩下的2个正整数的和不可能是1。

答案解析 

分几个步骤得到答案。

①有序枚举3个正整数可能的组合(1,1,6)(1,2,5)(1,3,4)(2,2,4)(2,3,3)。


②分成两类:                                                                                                                                     其中一类包含两个相同的数字,(1,1,6)(2,2,4)(2,3,3)每个可以形成8种队列快照。以(1,1,6)为例,(1)(6)(1,1)(1,6)(6,1)(1,1,6)(1,6,1)(6,1,1);
 另一类三个数字互不相同,(1,2,5)和(1,3,4)每个可以行程15种队列快照。以(1,2,5)为例(1)(2)(5)(1,2)(2,1)(1,5)(5,1)(2,5)(5,2)(1,2,5)(1,5,2)(2,1,5)(2,5,1)(5,2,1)(5,1,2)。
那么一共有3×8+2×15=543×8+2×15=54种,但是存在重复和遗漏。


③重复:单个数字的快照(1)重复了2次,(2)重复了2次, (3)重复了1次,(4)重复了1次,去掉重复后剩54−2−2−1−1=4854−2−2−1−1=48种;


④遗漏:遗漏了空的快照,加上后有48+1=4948+1=49种;


一共有49种可能的不同的队列快照。


 五.哥德巴赫猜想

题面

完善程序:
(哥德巴赫猜想)哥德巴赫猜想是指,任一大于2的偶数都可写成两个质数之和。迄今为止,这仍然是一个著名的世界难题,被誉为数学王冠上的明珠。试编写程序,验证任一大于2且不超过n的偶数都能写成两个质数之和。

#include <iostream>
using namespace std;

int main()
{
    const int SIZE = 1000;
        
    int n, r, p[SIZE], i, j, k, ans;
    bool tmp;
    
    cin>>n;
    r = 1;
    p[1] = 2;
    for (i = 3; i <= n; i++) {
        [    ①    ];
        for (j = 1; j <= r; j++)
            if (i % [     ②   ]  == 0) {
                tmp = false;
                break;
            }
        if (tmp) {
            r++;
            [    ③   ] ;
        }
    }
    
    ans = 0;
    for (i = 2; i <= n / 2; i++) {
        tmp = false;
        for (j = 1; j <= r; j++)
            for (k = j; k <= r; k++)
                if (i + i == [     ④   ] ) {
                    tmp = true;
                    break;
                }
        if (tmp)
            ans++;
    }
    cout<<ans<<endl;
    return 0;
}

若输入n为2010,则输出[ ⑤ ]时表示验证成功,即大于2且不超过2010的偶数都满足哥德巴赫猜想。

答案解析

根据直觉,我们可以知道,第一个循环求的是3-n里面所有的质数。

而由此可得,tmp标记的是当前的i是不是质数(true是,false非)

在①时,tmp需要初始化,变为1。

所以,①填的是:tmp=1或tmp=true或其他等价写法。

同时,p数组存的是质数,r是质数的个数,又因为i>p[j],且p[j]为质数,直接用i除p[j],这样就可以很快判断i是不是质数。

所以,第二题答案是p[j]

第三空:如果tmp仍为true,说明当前的i为质数,要记录新的质数了。因为记录质数个数的r已经加过一了(留出了位置),因此只需要将i赋值到p[r]即可即可。

所以,第三题答案是p[r]=i;

第四空:要判断2∗i(偶数)是否可以等于两质数(p[j]+p[k])之和

所以,第四题答案是p[j]+p[k]

第五空:若大于2且小于等于2010的偶数(有1004个)均可表示为两个质数之和,则应统计出1004个,输出的ans正是用于统计符合要求的偶数个数,所以填1004。

所以,第五题答案是1004.


完善程序:
(过河问题)在一个月黑风高的夜晚,有一群人在河的右岸,想通过唯一的一根独木桥走到河的左岸。在这伸手不见五指的黑夜里,过桥时必须借助灯光来照明,很不幸的是,他们只有一盏灯。另外,独木桥上最多承受两个人同时经过,否则将会坍塌。每个人单独过桥都需要一定的时间,不同的人需要的时间可能不同。两个人一起过桥时,由于只有一盏灯,所以需要的时间是较慢的那个人单独过桥时所花的时间。现输入(2≤n<100)和这n个人单独过桥时需要的时间,请计算总共最少需要多少时间,他们才能全部到达河的左岸。 例如,有3个人甲、乙、丙,他们单独过桥的时间分别为1、2、4,则总共最少需要的时间为7。具体方法是:甲、乙一起过桥到河的左岸,甲单独回到河的右岸将灯带回,然后甲、丙再一起过桥到河的左岸,总时间为2+1+4=72+1+4=7。

#include <iostream>
using namespace std;

const int SIZE = 100;
const int INFINITY = 10000;
const bool LEFT = true;
const bool RIGHT = false;
const bool LEFT_TO_RIGHT = true;
const bool RIGHT_TO_LEFT = false;

int n, hour[SIZE];
bool pos[SIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int go(bool stage)
{
    int i, j, num, tmp, ans;
    if (stage == RIGHT_TO_LEFT) {
        num = 0;
        ans = 0;
        for (i = 1; i <= n; i++)
            if (pos[i] == RIGHT) {
                num++;
                if (hour[i] > ans)
                    ans = hour[i];
            }
        if ([    ①    ])
            return ans;
        ans = INFINITY;
        for (i = 1; i <= n - 1; i++)
            if (pos[i] == RIGHT)
                for (j = i + 1; j <= n; j++)
                    if (pos[j] == RIGHT) {
                        pos[i] = LEFT;
                        pos[j] = LEFT;
                        tmp = max(hour[i], hour[j]) +[ ② ];
                        if (tmp < ans)
                           ans = tmp;
                        pos[i] = RIGHT;
                        pos[j] = RIGHT;
                    }
        return ans;
    }
    if (stage == LEFT_TO_RIGHT) {
        ans = INFINITY;
        for (i = 1; i <= n; i++)
            if ([    ③    ]) {
                pos[i] = RIGHT;
                tmp =[    ④    ];
                if (tmp < ans)
                    ans = tmp;
            [        ⑤    ];
            }
        return ans;
    }
    return 0;
}

int main()
{
    int i;
        
    cin>>n;
    for (i = 1; i <=n; i++) {
        cin>>hour[i];
        pos[i] = RIGHT;
    }
    cout<<go(RIGHT_TO_LEFT)<<endl;
    return 0;
}

解析:
在本题中所有人初始都在右边,需要从右走到左。LEFT表示在左边,用true代替,
LEFT ​TO ​RIGHT表示从左往右用true代替,RIGHT ​TO ​LEFT表示从右往左用false代替,go函数()分为两部分,一是从右往左走,一是从左往右走,最后的状态肯定是最后两个人从右往左走。
从右往左:第25~33行的是在寻找目前在右边的人的最长过桥时间ans和统计右边的人数num,当num<=2时,右边人全部走到左边任务结束了;第34~47行是通过递归将问题规模缩小,此次过桥时间+未知的后续过桥时间。
从左往右:显然只需要一个人,除了人数与从右往左走完全相同,判断人是否在左边,在就让他走到右边,将状态赋值为RIGHT,递归,再将状态改回LEFT。
①num<=2,由题意下一行可以直接return ans,说明此时已到达“基础情况”,即<=2num<=2(基础情况:只剩下两个人一趟可以走完);
go(LEFT_TO_RIGHT),使用()go()函数,即go(LEFT_TO_RIGHT),即进入回溯;
pos[i]==LEFT,说明此时在左岸;
hour[i]+go(RIGHT_TO_LEFT),此时tmp暂存值为已有的hour[i]加上go(RIGHT_TO_LEFT)所需时间;
pos[i]=LEFT,此时将位置pos[i]调整到LEFT。 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_L.Y.H._

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值