NOIP2015提高组初赛难点整理

选择题

对图 G G G 中各个结点分别指定一种颜色,使相邻结点颜色不同,则称为图 G G G 的一个正常着色。正常着色图 G G G 所必需的最少颜色数,称为 G G G 的色数。那么下图的色数是(3 )。

【解析】方法如下图所示:
在这里插入图片描述

  1. 以下图中一定可以进行黑白染色的有( AB )。(黑白染色:为各个结点分别指定黑白两种颜色之一,使相邻结点颜色不同。)
    A. 树
    B. 二分图
    C. 连通图
    D. 完全图

【解析】

  • 二分图可以进行黑白染色。二分图又称作二部图,是图论中的一种特殊模型。 设 G = ( V , E ) G=(V,E) G=(V,E)是一个无向图,如果顶点 V V V可分割为两个互不相交的子集( A A A, B B B),并且图中的每条边( i , j i,j ij)所关联的两个顶点ij分别属于这两个不同的顶点集(i in A A A,j in B B B),则称图 G G G为一个二分图。
    当且仅当图中不含奇数环(环中的边数是奇数),才能构成二分图。
  • 树也是二分图

1 1 1 2015 2015 2015 之间(包括 1 1 1 2015 2015 2015 在内)不能被 4 4 4 5 5 5 6 6 6 三个数任意一个数整除的数有(1075)个。

【解析】补集 + 容斥原理。

  • 能被 4 4 4整除的数有: ⌊ 2015 / 4 ⌋ = 503 \lfloor2015/4\rfloor=503 2015/4=503
  • 能被 5 5 5整除的数有: ⌊ 2015 / 5 ⌋ = 403 \lfloor2015/5\rfloor=403 2015/5=403
  • 能被 6 6 6整除的数有: ⌊ 2015 / 6 ⌋ = 335 \lfloor2015/6\rfloor=335 2015/6=335
  • 能被 20 20 20整除的数有: ⌊ 2015 / 20 ⌋ = 100 \lfloor2015/20\rfloor=100 2015/20=100
  • 能被 30 30 30整除的数有: ⌊ 2015 / 30 ⌋ = 67 \lfloor2015/30\rfloor=67 2015/30=67
  • 能被 12 12 12整除的数有: ⌊ 2015 / 12 ⌋ = 167 \lfloor2015/12\rfloor=167 2015/12=167
  • 能被 60 60 60整除的数有: ⌊ 2015 / 60 ⌋ = 33 \lfloor2015/60\rfloor=33 2015/60=33

那么,能被 4 4 4 5 5 5 6 6 6 三个数任意一个数整除的数有 940 940 940个。不能被 4 4 4 5 5 5 6 6 6 三个数任意一个数整除的数有 2015 − 740 = 1075 2015-740=1075 2015740=1075

阅读程序

#include <iostream>
using namespace std;

int fun(int n,int fromPos,int toPos)
{
    int t,tot;
    if(n==0)return 0;
    for(t=1;t<=3;t++)
        if(t!=fromPos && t != toPos)break;
    tot=0;
    tot+=fun(n-1,fromPos,t);
    tot++;
    tot+=fun(n-1,t,toPos);
    return tot;
}
int main()
{
    int n;
    cin>>n;
    cout<<fun(n,1,3)<<endl;
    return 0;
}

输入:5
输出:( 31

【解析】手动模拟找规律,打表记录模拟结果。

f(1,fromPos,toPos) f(2,fromPos,toPos) f(3,fromPos,toPos) f(4,fromPos,toPos) f(5,fromPos,toPos)
1 3 7 15 31

完善程序

(双子序列最大和)给定一个长度为 n ( 3 ≤ n ≤ 1000 ) n(3 \le n \le 1000 ) n(3n1000) 的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出这个最大和。一个连续子序列的序列和为该连续子序列中所有数之和。要求:每个连续子序列长度至少为 1 1 1,且两个连续子序列之间至少间隔 1 1 1 个数。(第五空 4 分,其余 2.5 分)

#include <iostrea m>
using namespace std;
const int MAXN = 1000;
int n, i, ans, sum;
int x[MAXN];
int lmax[MAXN];
// lmax[i] 为仅含 x[i] 及 x[i] 左侧整数的连续子序列的序列和中,最大的序列和
int rmax[MAXN];
// rmax[i] 为仅含 x[i] 及 x[i] 右侧整数的连续子序列的序列和中,最大的序列和

int main() {
    cin >> n;
    for (i = 0; i < n; i++) cin >> x[i];
    lmax[0] = x[0] ;
    for (i = 1; i < n; i++)
        if (lmax[i - 1] <= 0)
            lmax[i] = x[i];
        else
            lmax[i] = lmax[i - 1] + x[i];
    for (i = 1; i < n; i++)
        if (lmax[i] < lmax[i - 1])
            lmax[i] = lmax[i - 1];
    ①;
    for (i = n - 2; i >= 0; i --)
        if (rmax[i + 1] <= 0)
            ②;
        else
            ③;
    for (i = n - 2; i >= 0; i --)
        if (rmax[i] < rmax[i + 1])
            ④;
    ans = x[ 0] + x [2];
    for (i = 1; i < n - 1; i++) {
        sum = ⑤;
        if (sum > ans)
            ans = sum;
    }
    cout << ans << endl;
    return 0;
}

【解析】通过前缀和和贪心思想预处理出左边的最大连续子段和右边的最大连续子段和。枚举中间的数,然后将左边的最大子段的和加上右边的最大子段的和,打擂法,求出最大值。

  • 空①,初始化rmax[n - 1] = x[n - 1]
  • 空②,如果rmax[i + 1] <= 0,加上一个非正数,必然会使和减少,所以不如从当前位置开始计算连续子段和, rmax[i] = x[i];
  • 空③,如果rmax[i + 1] > 0,累加连续子段的和,rmax[i] = rmax[i + 1] + x[i]
  • 空④,与上面类似,更新成最大值,rmax[i] = rmax[i + 1]
  • 空⑤,枚举中间的数,然后将左边的最大子段的和加上右边的最大子段的和,打擂法,求出最大值。

猜你喜欢

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