[NOIP] 2018 普及组题解

目录

一、题解

1、T1(title)

2、T2(fight)

3、T3(bus)

4、T4(tree)

二、总结

三、代码

1、T1(title)

2、T2(fight) 

3、T3(bus) 

4、T4(tree)



一、题解


1、T1(title)

这道题就是一道送分题,可以直接用选择结构求解,但如果不想打太多代码,还是用循环结构看起来比较舒服

2、T2(fight)

这个题我们观察题目,首先可以拿到龙虎两方的初始势力,然后再来找应该在什么地方“天降神兵”

一个线性遍历,逐个枚举每一个兵营,再判断如果在此兵营“天降神兵”,我们已有的最小差值会不会比“天降神兵”后的最小差值大,再对其进行更新

但这道题不能就此了事,因为不知道是哪位出题人,再题目中写下了这样一句话:“第 m号兵营中的工兵很纠结,他们不属于任何一方。”

所以如果我们在任何一个兵营“天降神兵”,都不能使初始最小值更新,那么我们就应该在第m号兵营“天降神兵”,也就是直接输出m,至于为什么不能输出0,无可奉告

3、T3(bus)

这个题也不知道各位看出来是DP没有,这个题搞得我很开心鬼火冒,刚刚开始还以为是暴(mǒ)力(nǐ)

所以我就来讲讲这个题该如何模拟DP

首先是状态的定义,我们用一个三维数组来DP: 

我们用逆推的思想,dp[i][j][k]表示到第i个人时车还有j分钟到且现在有k个人在等车

我们现在来讲讲状态转移方程,因为我们输入后肯定要排序,此时就不妨将两个人之间等车的时间差拿到用wait数组保存

wait[i]表示第i个人到第i+1个人之见的时间差

(1) j > wait[i] (如图)

         

因为上一个状态中,也就是第i+1个人的状态中,车离第i+1个人的距离为j-wait[i],而等车的人数比现在第i个人的状态多一个,因为有k个人在等这班车,所以时间还要加上一个k*wait[i],此时:

dp[i][j][k] = dp[i + 1][j - wait[i]][k + 1] + k * wait[i]

(2) j <= wait[i]

第一种情况,第i+1个人也要坐这一班车(如图)

因为此时第i+1个人到了之后是不需要等车的,所以他刚刚到就可以坐上车,所以第i+1个人的状态中,车已经在等他了,所以j就为0,等车人数当然也要多一个,和上一种情况一样,也有k个人在等车,时间也要加上一个k*wait[i],此时:

dp[i][j][k] = dp[i + 1][0][k + 1] + k * wait[i]

第二种情况,第i+1个人不坐这班车(如图)

此时可以得出,上一个状态,也就是第i+1个人的状态车还有j+m-wait[i]分钟才能到,因为第i+1个人不坐这班车,所以上一状态等车的人就只有一个了,此时dp[i][j][k]=dp因为之前等车的k个人都需要等j分钟,所以要加上k*j

我们再来讨论一下下面的这种情况(如图)

此时我们会发现j+m-wait[i]是一个负数,显然数组会越界,其实处理方法很简单,加一个绝对值就行了

用脑子想一下,怎么可能是绝对值,我们表示的是上一个状态车还有j+m-wait[i]分钟到,就按这种情况来说,就不能是取绝对值,因为第i+1个人根本就不用等车,所以应该是和0来作比较,此时:

dp[i][j][k] = dp[i + 1][max(0, j + m - wait[i])][1] 

因为是逆推,初始值就放在最后来讲,我们还是借用一个图来理解:

此时肯定是第n个人的状态,假设车还有i分钟到,此时有j人在等车,那么:

dp[n][i][j] = i * j

注意在最后,我们只需要输出dp[1][0][1],即第1个人状态车还有0分钟到且只有一个人(他自己)在等车 

4、T4(tree)

这道题是一道dfs,方法比较暴力,概括地讲,就是枚举每一个点,然后再看以这个点为根节点的子树是否是一棵对称二叉树就可以了

相信每个点的遍历大家都知道,最主要的就是如何去判断这棵树是否为一棵对称二叉树

首先我们定义一个函数,其中有两个参数,分别表示两个节点(左边的记为L,右边的记为R,并保证它们在同一深度且之前的所有节点都对称),在两个节点的点权都相等的情况下我们分成以下几种情况来讨论:

(1)两个节点都没有左右儿子

此时肯定是对称二叉树,不需要继续往下判断形状

(2)两个节点都有左右儿子

此时我们只能判断L节点和R节点的形状是对称的,但我们并不能判断之后的所有节点是否对称,所以我们要进行一个递归的处理,即递归判断L节点的左子树和R节点的右子树,还有L节点的右子树和R节点的左子树是否对称即可

(3)L节点有右儿子无左儿子,R节点有左儿子无右儿子

此时也要递归处理,但没有那么多情况,只需要递归判断L节点的右子树和R节点的左子树是否对称即可

(4)L节点有左儿子无右儿子,R节点有右儿子无左儿子

同样,此时递归判断L节点的左子树和R节点的右子树即可 

如果是其它情况,那么这棵树的形状都不能对称,肯定就不是对称二叉树了

当然,如果直接这样做,肯定不可能AC的,毕竟也是一道压轴题,我们需要进行一点优化,不难得出一下结论

如果L节点和R节点的所有子节点个数不同,我们就可以直接得出,这两棵子树肯定不是对称的 

处理方式也很简单,只需要用一个数组来保存这个节点所有子节点的个数,而它子节点的个数就等于它左儿子和右儿子的所有子节点个数和加上它自己

二、总结


这次是我第一次参加NOIP,考场上的状态很不好,分数下来也是特别烂,感到深深的绝望,把我从半期成绩得来的喜悦全部冲没了

同时充分暴露出我对算法的掌握很差很差

所以,从此以后,我要好好学习,天天向上,毕竟还有一年的时间,相信一年的努力,不管是实力还是应考技巧等等的能力都会得到提高,争取取得一个好成绩吧

三、代码


建议不看,先自己尝试(其实是因为代码写的比较烂)

1、T1(title)

#include<cstdio>
#include<cstring>
#define reg register
#define M 105

char a[M];
int len, ans;

int main(){
    gets(a);
    len = strlen(a);
    for(reg int i = 0;i <= len - 1;i ++){
        if(a[i] >= '0' && a[i] <= '9')
            ans ++;
        if(a[i] >= 'A' && a[i] <= 'Z')
            ans ++;
        if(a[i] >= 'a' && a[i] <= 'z')
            ans ++;
    }
    printf("%d\n", ans);
    return 0;
}

2、T2(fight) 

#include<cstdio>
#include<cstring>
#define reg register
#define LL long long
#define M 100005

int n,m,p1,p2;

LL a[100005],s1,s2;
LL Dtot,Ttot,x,P;

inline void read(LL &x){
    x = 0; LL f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = x * 10 + s - 48; s = getchar();}
    x *= f;
}

LL fabs(LL x){
    if(x < 0)
        return -x;
    return x;
}
int main(){
    scanf("%d", &n);
    for(reg int i = 1;i <= n;i ++)
        read(a[i]);
    scanf("%d%d", &m, &p1);
    read(s1), read(s2);
    a[p1] += s1;
    for(reg int i = 1;i <= n;i ++){
        if(i < m)
            Dtot += (m - i) * a[i];
        if(i>m)
            Ttot += (i - m) * a[i];
    }
    P = fabs(Dtot - Ttot);
    p2 = m;
    for(reg int i = 1;i <= n;i ++){
        if(i < m){
            x = (m - i) * s2;
            if(fabs(Dtot + x - Ttot) < P)
                p2 = i, P = fabs(Dtot + x - Ttot);
        }
        if(i > m){
            x = (i - m) * s2;
            if(fabs(Ttot + x - Dtot) < P)
                p2 = i, P = fabs(Ttot + x - Dtot);
        }
    }
    printf("%d\n", p2);
    return 0;
}

3、T3(bus) 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define reg register
#define max(a, b) a > b ? a : b
#define min(a, b) a < b ? a : b
#define M 505

int n, m;
int a[M], wait[M], dp[M][M][M];

inline void read(int &x){
    x = 0; int f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = x * 10 + s - 48; s = getchar();}
    x *= f;
}
int main(){
    read(n), read(m);
    for(reg int i = 1;i <= n;i ++)
        read(a[i]);
    std::sort(a + 1, a + 1 + n);
    for(reg int i = 1;i <= n - 1;i ++)
        wait[i] = a[i + 1] - a[i];
    for(reg int i = 0;i <= m;i ++)
        for(reg int j = 1;j <= n;j ++)
            dp[n][i][j] = i * j;
    for(reg int i = n - 1;i >= 1;i --)
        for(reg int j = 0;j <= m;j ++)
            for(reg int k = 1;k <= i;k ++){
                if(j > wait[i])
                    dp[i][j][k] = dp[i + 1][j - wait[i]][k + 1] + k * wait[i];
                else
                    dp[i][j][k] = min(dp[i + 1][0][k + 1] + k * wait[i], dp[i + 1][max(0, j + m - wait[i])][1] + k * j);
            }
    printf("%d\n", dp[1][0][1]);
    return 0;
}

4、T4(tree)

#include<cstdio>
#define reg register
#define max(a, b) a > b ? a : b
#define M 1000005

struct node{
    int l, r, v;
};
node a[M];

int n, ans = 1;
int num[M];

bool flag[M];

inline bool right(int l,int r){
    if(l == 0 && r == 0)
        return 0;
    if(num[l] != num[r])
        return 0;
    if(a[l].l == 0 && a[l].r == 0 && a[r].l == 0 && a[r].r == 0 && a[l].v == a[r].v)
        return 1;
    else{
        if(a[l].l == 0 && a[r].r == 0 && a[l].r != 0 && a[r].l != 0 && a[l].v == a[r].v)
            return right(a[l].r, a[r].l);
        else{
            if(a[l].r == 0 && a[r].l == 0 && a[l].l != 0 && a[r].r != 0 && a[l].v == a[r].v)
                return right(a[l].l, a[r].r);
            else{
                if(a[l].r != 0 && a[l].l != 0 && a[r].l != 0 && a[r].r != 0 && a[l].v == a[r].v)
                    return right(a[l].l, a[r].r) && right(a[l].r, a[r].l);
                else
                    return 0;
            }
        }
    }
}
inline void dfs(int idx){
    if(a[idx].l != 0)
        dfs(a[idx].l);
    if(a[idx].r != 0)
        dfs(a[idx].r);
    num[idx] = 1 + num[a[idx].l] + num[a[idx].r];
    if(num[idx] > ans && right(a[idx].l,a[idx].r))
        ans = max(ans, num[idx]);
}
int main(){
    scanf("%d", &n);
    for(reg int i = 1;i <= n;i ++){
        scanf("%d", &a[i].v);
        num[i] = 1;
    }
    for(reg int i = 1;i <= n;i ++){
        scanf("%d%d", &a[i].l, &a[i].r);
        if(a[i].l == -1)
            a[i].l = 0;
        if(a[i].r == -1)
            a[i].r = 0;
    }
    dfs(1);
    printf("%d\n", ans);
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/weixin_43896346/article/details/84673944