NOIP2014普及组初赛难点整理

问题求解

  1. M M M 个同样的球放到 N N N 个同样的袋子里,允许有的袋子空着不放,问共有多少种不同的放置方法?(用 K K K 表示)。例如, M = 7 M=7 M=7 N = 3 N=3 N=3 时, K = 8 K=8 K=8;在这里认为 ( 5 , 1 , 1 ) (5,1,1) (5,1,1) ( 1 , 5 , 1 ) (1,5,1) (1,5,1) 是同一种放置方法。
    问: M = 8 M=8 M=8 N = 5 N=5 N=5 时, K = K= K=____。

【解析】 n n n个相同的球放入 m m 个相同的盒子( n ≥ m n≥m nm),可以有空盒时的放法种数等于将 n n n分解为 m m 个、 ( m - 1 ) (m-1) (1)个、 ( m - 2 ) (m-2) (2)个、…、 2 2 2个、 1 1 1个数的和的所有种数之和。
8分解为 1 1 1个数:8,共 1 1 1种 将8分解为 2 2 2个数:1+72+63+54+5,共 4 4 4
8分解为 3 3 3个数:1+1+61+2+51+3+42+2+42+3+3,共 5 5 5
8分解为 4 4 4个数:1+1+1+51+1+2+41+1+3+31+2+2+32+2+2+2,共 5 5 5
8分解为 5 5 5个数:1+1+1+1+41+1+1+2+31+1+2+2+2,共 3 3 3
答案:18

  1. 如图所示,图中每条边上的数字表示该边的长度,则从 A A A E E E 的最短距离是____。
    在这里插入图片描述

【解析】正权图的最短路,可以使用Dijkstra算法求解。

阅读程序写结果

#include <iostream>
using namespace std;
int fun(int n)
{
    if(n == 1)
        return 1;
    if(n == 2)
        return 2;
    return fun(n - 2) - fun(n - 1);
}
int main()
{
    int n;
    cin >> n;
    cout << fun(n) << endl;
    return 0;
}

输入:7

【解析】模拟递归调用的结果如下:

f(1) f(2) f(3) f(4) f(5) f(6) f(7)
1 2 -1 3 -4 7 -11

输出:-11
2.

#include <iostream>
using namespace std;
const int SIZE = 100;
int main()
{
    int p[SIZE];
    int n, tot, i, cn;
    tot = 0;
    cin >> n;
    for(i = 1; i <= n; i++)
        p[i] = 1;
    for(i = 2; i <= n; i++)
    {
        if(p[i] == 1)
            tot++;
        cn = i * 2;
        while(cn <= n)
        {
            p[cn] = 0;
            cn += i;
        }
    }
    cout << tot << endl;
    return 0;
}

输入:30
输出:10

【解析】埃氏筛素数法。 对正实数 x x x,定义 π ( x ) π(x) π(x)为不大于 x x x的素数个数, π ( x ) ≈ x / l n x π(x)≈x/ln x π(x)x/lnx
其中 l n x = l o g e x lnx=log_e^x lnx=logex,为 x x x的自然对数, e = 2.718281828... e=2.718281828... e=2.718281828... l n 30 ≈ 3 ln30≈3 ln303

完善程序

  1. (最大子矩阵和)给出 m m m n n n 列的整数矩阵,求最大的子矩阵和(子矩阵不能为空)。
    输入第一行包含两个整数 m m m n n n,即矩阵的行数和列数。之后 m m m 行,每行 n n n 个整数,描述整个矩阵。程序最终输出最大的子矩阵和。
#include <iostream>
using namespace std;
const int SIZE = 100;
int matrix[SIZE + 1][SIZE + 1];
int rowsum[SIZE + 1][SIZE + 1]; //rowsum[i][j]记录第 i 行前 j 个数的和
int m, n, i, j, first, last, area, ans;
int main()
{
    cin >> m >> n;
    for(i = 1; i <= m; i++)
        for(j = 1; j <= n; j++)
            cin >> matrix[i][j];
    ans = matrix ①;
    for(i = 1; i <= m; i++)
        ②
    for(i = 1; i <= m; i++)
        for(j = 1; j <= n; j++)
            rowsum[i][j] = ③;
    for(first = 1; first <= n; first++)
        for(last = first; last <= n; last++)
        {
            ④;
            for(i = 1; i <= m; i++)
            {
                area += ⑤;
                if(area > ans)
                    ans = area;
                if(area < 0)
                    area = 0;
            }
        }
    cout << ans << endl;
    return 0;
}

【解析】利用前缀和的思想,计算出矩阵第i行前j列的和rowsum[i][j]。枚举所有列的组合,求出子矩阵和的最大值。

  • 空①,初始化ans,让其等于矩阵第一行第一列的值matrix[1][1],所以应填入[1][1]
  • 空②,初始化每行前缀和数组的第0项,将其置为 0 0 0,用于之后计算前缀和,所以应填入rowsum[i][0]=0
  • 空③,计算第i行的前缀和,所以应填入rowsum[i][j-1]+matrix[i][j]
  • 空④,初始化area 0 0 0,所以应填入area=0
  • 空⑤,使用前缀和数组计算first列到last列的和,rowsum[i][last]-rowsum[i][first-1]

猜你喜欢

转载自blog.csdn.net/qiaoxinwei/article/details/107889469