【CSP历年题解】

2013-12


2013-12-1-出现次数最多的数

题目链接
http://118.190.20.162/view.page?gpid=T5
题解
我 们 扫 描 一 遍 整 个 序 列 , 每 次 增 加 当 前 数 字 的 个 数 我们扫描一遍整个序列,每次增加当前数字的个数
若 当 前 数 字 个 数 大 于 当 前 答 案 出 现 的 次 数 , 则 更 新 答 案 若当前数字个数大于当前答案出现的次数,则更新答案
若 当 前 数 字 个 数 等 于 当 前 答 案 的 出 现 次 数 并 且 当 前 数 字 小 于 当 前 答 案 , 则 更 新 答 案 若当前数字个数等于当前答案的出现次数并且当前数字小于当前答案,则更新答案
本 题 坑 点 : 本题坑点:
① : 出 现 次 数 相 同 保 留 更 小 的 ①:出现次数相同保留更小的 :
② : 数 据 范 围 是 10000 ②:数据范围是10000 :10000
代码

/*
***********************************************
Author        :biubiubiu
Created Time  :2018/10/17/03:02
File Name     :F:\CSP\2013-12\1.cpp
************************************************
*/
#include
#include
#include
using namespace std;
const int maxn = 1e4+10;
int sum[maxn];
int main()
{
    int x,n,ans,maxx=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        sum[x]++;
        if(sum[x]>maxx)//保留出现次数较多的
        {
            maxx=sum[x];
            ans=x;
        }
        else if(sum[x]==maxx)//出现次数相同保留较小的
        {
            if(x<ans) ans=x;
        }
    }
    printf("%d\n",ans);
    return 0;
}

2013-12-2-ISBN号码

题目链接
http://118.190.20.162/view.page?gpid=T4
题解
我 们 对 整 个 序 列 边 加 边 取 模 即 可 我们对整个序列边加边取模即可
对 于 每 个 位 置 直 接 加 上 他 的 贡 献 并 取 模 对于每个位置直接加上他的贡献并取模
由 于 由于
( a % M o d + b % M o d ) % M o d = ( a + b ) % M o d \left( a\%Mod+b\%Mod \right) \%Mod=\left( a+b \right) \%Mod (a%Mod+b%Mod)%Mod=(a+b)%Mod
( a % M o d ∗ b % M o d ) % M o d = ( a ∗ b ) % M o d \left( a\%Mod*b\%Mod \right) \%Mod=\left( a*b \right) \%Mod (a%Modb%Mod)%Mod=(ab)%Mod
所 以 我 们 每 次 算 完 当 前 位 贡 献 直 接 取 模 即 可 。 所以我们每次算完当前位贡献直接取模即可。
最 后 判 断 是 否 为 合 法 识 别 码 最后判断是否为合法识别码

本 题 坑 点 : 本题坑点:
① : 结 果 为 10 保 留 为 X ①:结果为10保留为X :10X
② : 原 串 中 最 后 一 位 也 可 能 为 X ②:原串中最后一位也可能为X :X
代码

/*
***********************************************
Author        :biubiubiu
Created Time  :2018/10/17/03:05
File Name     :F:\CSP\2013-12\2.cpp
************************************************
*/
#include
#include
#include
using namespace std;
char str[20];
int main()
{
    scanf("%s",str);
    int sum=0;
    sum=(sum+(str[0]-'0')*1)%11;
    sum=(sum+(str[2]-'0')*2)%11;
    sum=(sum+(str[3]-'0')*3)%11;
    sum=(sum+(str[4]-'0')*4)%11;
    sum=(sum+(str[6]-'0')*5)%11;
    sum=(sum+(str[7]-'0')*6)%11;
    sum=(sum+(str[8]-'0')*7)%11;
    sum=(sum+(str[9]-'0')*8)%11;
    sum=(sum+(str[10]-'0')*9)%11;
    //以上算出每一位的贡献并边算边取模
    if(sum==str[12]-'0'||(sum==10&&str[12]=='X')) printf("Right");//注意原数据中可能有X
    else
    {
        for(int i=0;i<12;i++) printf("%c",str[i]);
        if(sum==10) printf("X");//特判10的情况
        else printf("%c",char(sum+'0'));
    }
    return 0;
}

2013-12-3-最大的矩形

题目链接
http://118.190.20.162/view.page?gpid=T3
题解
本 题 是 经 典 的 单 调 栈 问 题 本题是经典的单调栈问题
我 们 定 义 l [ i ] 为 i 左 侧 第 一 个 高 度 小 于 h [ i ] 的 下 标 , r [ i ] 为 i 右 侧 第 一 个 高 度 小 于 h [ i ] 的 下 标 我们定义l[i]为i左侧第一个高度小于h[i]的下标,r[i]为i右侧第一个高度小于h[i]的下标 l[i]ih[i]r[i]ih[i]
首 先 我 们 发 现 当 后 面 的 长 方 形 比 前 面 的 长 方 形 低 时 是 没 有 意 义 的 首先我们发现当后面的长方形比前面的长方形低时是没有意义的
所 以 我 们 维 护 一 个 单 调 递 增 的 栈 所以我们维护一个单调递增的栈
栈 中 存 放 的 是 满 足 单 调 递 增 性 质 的 下 标 栈中存放的是满足单调递增性质的下标
当 前 元 素 小 于 栈 顶 栈 顶 弹 出 , 并 更 新 栈 顶 的 r 数 组 当前元素小于栈顶栈顶弹出,并更新栈顶的r数组 r
同 理 我 们 可 以 处 理 出 l 数 组 。 之 后 本 题 就 解 决 了 同理我们可以处理出l数组。之后本题就解决了 l
本 题 坑 点 本题坑点
① : 单 调 栈 为 空 时 直 接 加 入 元 素 ①:单调栈为空时直接加入元素 :
② : 弹 出 栈 时 先 判 定 栈 是 否 为 空 ②:弹出栈时先判定栈是否为空 :
代码

/*
***********************************************
Author        :biubiubiu
Created Time  :2018/10/17/03:15
File Name     :F:\CSP\2013-12\3.cpp
************************************************
*/
#include
#include
#include
#include
using namespace std;
const int maxn = 1e4+10;
int h[maxn],l[maxn],r[maxn];
stack<int> stk;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&h[i]);
    for(int i=1;i<=n;i++)
    {
        l[i]=1;
        r[i]=n;
    }
    for(int i=1;i<=n;i++)
    {
        if(stk.empty()) stk.push(i);
        else
        {
            if(h[i]>=h[stk.top()])
            {
                stk.push(i);
            }
            else
            {
                while(!stk.empty()&&h[stk.top()]>h[i])
                {
                    r[stk.top()]=i-1;
                    stk.pop();
                }
                stk.push(i);
            }
        }
    }
    while(!stk.empty()) stk.pop();
    for(int i=n;i>=1;i--)
    {
        if(stk.empty()) stk.push(i);
        else
        {
             if(h[i]>=h[stk.top()])
            {
                stk.push(i);
            }
            else
            {
                while(!stk.empty()&&h[stk.top()]>h[i])
                {
                    l[stk.top()]=i+1;
                    stk.pop();
                }
                stk.push(i);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++) ans=max(ans,(r[i]-l[i]+1)*h[i]);
    printf("%d",ans);
    return 0;
}

2013-12-4-有趣的数

题目链接
http://118.190.20.162/view.page?gpid=T2
题解
本 题 考 察 点 在 于 观 察 满 足 性 质 的 序 列 种 类 , 发 现 种 类 并 不 多 本题考察点在于观察满足性质的序列种类,发现种类并不多
一 共 有 13 个 合 法 状 态 , 于 是 我 们 只 要 可 以 推 理 出 13 个 合 法 状 态 之 间 的 转 移 方 程 就 可 以 。 一共有13个合法状态,于是我们只要可以推理出13个合法状态之间的转移方程就可以。 1313
首 先 我 们 给 出 13 个 合 法 状 态 : 首先我们给出13个合法状态: 13
1 由 1 转 移 − − − − 1 状 态 1 由1转移 ----1状态 111
2 由 2 转 移 − − − − 2 状 态 2 由 2 转移 ----2状态 222
3 由 3 转 移 − − − − 3 状 态 3 由 3 转移 ----3状态 333
02 由 2   4 转 移 − − − − 4 状 态 02 由 2 \ 4转移 ----4状态 022 44
03 由 3   5 转 移 − − − − 5 状 态 03 由 3 \ 5 转移 ----5状态 033 55
12 由 1   2   6 转 移 − − − − 6 状 态 12 由1 \ 2 \ 6 转移 ----6状态 121 2 66
13 由 1   3   7 转 移 − − − − 7 状 态 13 由1 \ 3 \ 7转移 ----7状态 131 3 77
23 由 2   8 转 移 − − − − 8 状 态 23 由 2 \ 8 转移 ----8状态 232 88
012 由 4   9 转 移 − − − − 9 状 态 012 由 4 \ 9 转移 ----9状态 0124 99
013 由 5   10 转 移 − − − − 10 状 态 013由 5 \ 10 转移 ----10状态 0135 1010
023 由 4   8   11 转 移 − − − − 11 状 态 023 由 4 \ 8 \ 11转移 ----11状态 0234 8 1111
123 由 6   12 转 移 − − − − 12 状 态 123 由 6 \ 12 转移 ----12状态 1236 1212
0123 由 9   11   13 转 移 − − − − 13 状 态 0123 由9 \ 11 \ 13 转移 ----13状态 01239 11 1313
我 们 定 义 d p [ i ] [ j ] 为 数 字 长 度 为 i 状 态 为 j 的 数 字 种 类 数 我们定义dp[i][j]为数字长度为i状态为j的数字种类数 dp[i][j]ij
很 明 显 初 始 状 态 的 定 义 为 : 很明显初始状态的定义为:
d p [ 1 ] [ 1 ] = 1 , d p [ 1 ] [ 2 ] = 1 , d p [ 1 ] [ 3 ] = 1 ; dp[1][1]=1,dp[1][2]=1,dp[1][3]=1; dp[1][1]=1,dp[1][2]=1,dp[1][3]=1;
我 们 很 容 易 得 到 状 态 转 移 方 程 : 我们很容易得到状态转移方程:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] ; dp[i][1]=dp[i-1][1]; dp[i][1]=dp[i1][1];
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 2 ] ; dp[i][2]=dp[i-1][2]; dp[i][2]=dp[i1][2];
d p [ i ] [ 3 ] = d p [ i − 1 ] [ 3 ] ; dp[i][3]=dp[i-1][3]; dp[i][3]=dp[i1][3];
d p [ i ] [ 4 ] = d p [ i − 1 ] [ 2 ] + 2 ∗ d p [ i − 1 ] [ 4 ] ; dp[i][4]=dp[i-1][2]+2*dp[i-1][4]; dp[i][4]=dp[i1][2]+2dp[i1][4];
d p [ i ] [ 5 ] = d p [ i − 1 ] [ 3 ] + 2 ∗ d p [ i − 1 ] [ 5 ] ; dp[i][5]=dp[i-1][3]+2*dp[i-1][5]; dp[i][5]=dp[i1][3]+2dp[i1][5];
d p [ i ] [ 6 ] = d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] + 2 ∗ d p [ i − 1 ] [ 6 ] ; dp[i][6]=dp[i-1][1]+dp[i-1][2]+2*dp[i-1][6]; dp[i][6]=dp[i1][1]+dp[i1][2]+2dp[i1][6];
d p [ i ] [ 7 ] = d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 3 ] + 2 ∗ d p [ i − 1 ] [ 7 ] ; dp[i][7]=dp[i-1][1]+dp[i-1][3]+2*dp[i-1][7]; dp[i][7]=dp[i1][1]+dp[i1][3]+2dp[i1][7];
d p [ i ] [ 8 ] = d p [ i − 1 ] [ 2 ] + d p [ i − 1 ] [ 8 ] ; dp[i][8]=dp[i-1][2]+dp[i-1][8]; dp[i][8]=dp[i1][2]+dp[i1][8];
d p [ i ] [ 9 ] = d p [ i − 1 ] [ 4 ] + 2 ∗ d p [ i − 1 ] [ 9 ] ; dp[i][9]=dp[i-1][4]+2*dp[i-1][9]; dp[i][9]=dp[i1][4]+2dp[i1][9];
d p [ i ] [ 10 ] = d p [ i − 1 ] [ 5 ] + 2 ∗ d p [ i − 1 ] [ 10 ] ; dp[i][10]=dp[i-1][5]+2*dp[i-1][10]; dp[i][10]=dp[i1][5]+2dp[i1][10];
d p [ i ] [ 11 ] = d p [ i − 1 ] [ 4 ] + d p [ i − 1 ] [ 8 ] + 2 ∗ d p [ i − 1 ] [ 11 ] ; dp[i][11]=dp[i-1][4]+dp[i-1][8]+2*dp[i-1][11]; dp[i][11]=dp[i1][4]+dp[i1][8]+2dp[i1][11];
d p [ i ] [ 12 ] = d p [ i − 1 ] [ 6 ] + 2 ∗ d p [ i − 1 ] [ 12 ] ; dp[i][12]=dp[i-1][6]+2*dp[i-1][12]; dp[i][12]=dp[i1][6]+2dp[i1][12];
d p [ i ] [ 13 ] = d p [ i − 1 ] [ 9 ] + d p [ i − 1 ] [ 11 ] + 2 ∗ d p [ i − 1 ] [ 13 ] ; dp[i][13]=dp[i-1][9]+dp[i-1][11]+2*dp[i-1][13]; dp[i][13]=dp[i1][9]+dp[i1][11]+2dp[i1][13];
最 终 d p [ n ] [ 13 ] 即 为 最 终 答 案 。 最终dp[n][13]即为最终答案。 dp[n][13]
本 题 坑 点 : 本题坑点:
① : 同 状 态 之 间 的 转 移 一 定 要 注 意 不 能 使 用 本 状 态 不 存 在 的 元 素 ①:同状态之间的转移一定要注意不能使用本状态不存在的元素 :使
② : 不 同 状 态 之 间 的 转 移 注 意 先 后 关 系 ②:不同状态之间的转移注意先后关系 :
③ : 注 意 不 能 存 在 前 导 0 的 问 题 ③:注意不能存在前导0的问题 :0
代码

/*
***********************************************
Author        :biubiubiu
Created Time  :2018/10/17/03:46
File Name     :F:\CSP\2013-12\4.cpp
************************************************
*/
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1005;
const int Mod =1000000007;
ll dp[maxn][14];//dp[i][j]表示到达i长度满足j状态的种类数
int main()
{
    /*--------------------
    一个
    1      1       ----1状态
    2      2       ----2状态
    3      3       ----3状态
    两个
    02     2 4     ----4状态
    03     3 5     ----5状态
    12     1 2 6   ----6状态
    13     1 3 7   ----7状态
    23     2 8     ----8状态
    三个
    012    4 9      ----9状态
    013    5 10     ----10状态
    023    4 8 11   ----11状态
    123    6 12     ----12状态
    四个
    0123   9 11 13  ----13状态
    */
    dp[1][1]=1,dp[1][2]=1,dp[1][3]=1;
    int n;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        dp[i][1]=dp[i-1][1];
        dp[i][2]=dp[i-1][2];
        dp[i][3]=dp[i-1][3];
        dp[i][4]=dp[i-1][2]+2*dp[i-1][4];
        dp[i][5]=dp[i-1][3]+2*dp[i-1][5];
        dp[i][6]=dp[i-1][1]+dp[i-1][2]+2*dp[i-1][6];
        dp[i][7]=dp[i-1][1]+dp[i-1][3]+2*dp[i-1][7];
        dp[i][8]=dp[i-1][2]+dp[i-1][8];
        dp[i][9]=dp[i-1][4]+2*dp[i-1][9];
        dp[i][10]=dp[i-1][5]+2*dp[i-1][10];
        dp[i][11]=dp[i-1][4]+dp[i-1][8]+2*dp[i-1][11];
        dp[i][12]=dp[i-1][6]+2*dp[i-1][12];
        dp[i][13]=dp[i-1][9]+dp[i-1][11]+2*dp[i-1][13];
        for(int j=1;j<=13;j++) dp[i][j]%=Mod;
    }
    printf("%lld",dp[n][13]);
    return 0;
}


2013-12-5-I’m stuck!

题目链接
http://118.190.20.162/view.page?gpid=T1
题解
本 题 是 标 准 的 爆 搜 问 题 , 由 于 数 据 量 < 50 , d f s 或 者 b f s 均 可 解 决 本 问 题 本题是标准的爆搜问题,由于数据量<50,dfs或者bfs均可解决本问题 <50,dfsbfs
我 们 先 处 理 出 所 有 起 点 能 到 达 的 点 并 进 行 标 记 我们先处理出所有起点能到达的点并进行标记
再 处 理 出 所 有 终 点 能 到 达 的 点 并 标 记 , 最 后 统 计 答 案 即 可 。 再处理出所有终点能到达的点并标记,最后统计答案即可。
本 题 坑 点 : 本题坑点: :
① : 起 点 不 能 到 达 终 点 直 接 输 出 “ I ′ m s t u c k ! ” ①:起点不能到达终点直接输出“I'm stuck!” :Imstuck!
② : d f s 时 的 边 界 问 题 ②:dfs时的边界问题 :dfs
③ : 对 于 不 同 字 符 的 不 同 处 理 方 式 ③:对于不同字符的不同处理方式 :
代码

/*
***********************************************
Author        :biubiubiu
Created Time  :2018/10/17/04:20
File Name     :F:\CSP\2013-12\5.cpp
************************************************
*/
#include
#include
#include
#include
using namespace std;
#define maxn 50
int R,C;
char G[maxn][maxn];
bool vis1[maxn][maxn];//起点能到达的矩阵
bool vis2[maxn][maxn];//终点能到的矩阵
//正向深搜,从起点出发
void dfs1(bool sign[maxn][maxn], int curR, int curC)
{

    int Row=R,Col=C;
    //若 当前点已走过 或 当前点是障碍物,停止递归
    if (sign[curR][curC]||G[curR][curC]=='#')
    {
        return ;
    }
    //标记此点可达
    sign[curR][curC]=true;
    //标记可以往哪个方向移动
    bool up,down,left,right;
    up=down=left=right=false;
    if(G[curR][curC]=='.')//向下移动
    {
        down = true;
    }
    else if (G[curR][curC]=='-') //左右移动
    {
        left=right=true;
    }
    else if(G[curR][curC]=='|') //上下移动
    {
        up=down=true;
    }
    else if(G[curR][curC]=='+'||G[curR][curC]=='S'||G[curR][curC]=='T') //上下左右移动
    {
        up=down=left=right=true;
    }
    //上
    if(up&&curR-1>=0)
    {
        dfs1(sign,curR-1,curC);
    }
    //下
    if(down&&curR+1<Row)
    {
        dfs1(sign,curR+1,curC);
    }
    //左
    if(left&&curC-1>=0)
    {
        dfs1(sign,curR,curC-1);
    }
    //右
    if(right&&curC+1<Col)
    {
        dfs1(sign,curR,curC+1);
    }
}
//从终点出发,反向DFS
// 反向搜索的方法不能跟之前正向一样,假设T上面一个-,按照正向那么是可以走通的,但是对于反向来说,从-显然不能走到下方的T。
// 所以反向是从上一点试探性的向上下左右走一步到达下一点,然后以下一点为基准判断是否能走到上一点。
void dfs2(bool sign[maxn][maxn],int curR,int curC,/*当前点 */int preR, int preC /*上一个点*/)
{
    int Row=R,Col=C;
    //若 当前点已走过 或 当前点是障碍物,停止递归
    if(sign[curR][curC]||G[curR][curC]=='#')
    {
        return ;
    }
    //从下面来的,可以返回去,其他方向来的无法原路返回
    if(G[curR][curC]=='.'&&preR==curR+1&&preC==curC)
    {
        sign[curR][curC]=true;
    }
    else if(G[curR][curC]=='-'&&preR==curR)
    {
        sign[curR][curC]=true;
    }
    else if(G[curR][curC]=='|'&&preC==curC)
    {
        sign[curR][curC]=true;
    }
    else if(G[curR][curC]=='S'||G[curR][curC]=='+'||G[curR][curC]=='T')
    {
        sign[curR][curC] = true;
    }
    //没有方式可以从下一点走到上一点,那么无需再试探,直接返回
    if (sign[curR][curC] == false)
    {
        return ;
    }
    //上
    if (curR-1 >= 0)
    {
        dfs2(sign,curR-1,curC,curR,curC);
    }
    //下
    if (curR+1 < Row)
    {
        dfs2(sign,curR+1,curC,curR,curC);
    }
    //左
    if (curC-1 >= 0)
    {
        dfs2(sign,curR,curC-1,curR,curC);
    }
    //右
    if (curC+1 < Col)
    {
        dfs2(sign,curR,curC+1,curR,curC);
    }
}
int main()
{
    scanf("%d%d",&R,&C);
    for(int i=0;i<R;i++)
    {
        for(int j=0;j<C;j++)
        {
            scanf("%d",&G[i][j]);
            if (G[i][j] == 'S') dfs1(vis1,i,j);
        }
    }
    for(int i=0;i<R;i++)
    {
        for (int j=0;j<C;j++)
        {
            if(G[i][j]=='T')
            {
                // 如果起点到终点都没有那么直接输出
                if(vis1[i][j]==false)
                {
                    printf("I'm stuck!\n");
                    return 0;
                }
                dfs2(vis2,i,j,i,j);
            }
        }
    }
    int num=0;
    for(int i=0;i<R;i++)
    {
        for(int j=0;j<C;j++)
        {
            // 注意除去自身
            if(G[i][j]!='S'&&G[i][j]!='T'&&vis1[i][j]&&!vis2[i][j])  num++;
        }
    }
    printf("%d\n",num);
    return 0;
}


你可能感兴趣的:(CSP)