2017年第八届蓝桥杯【C++省赛B组】

1.标题: 购物单


    小明刚刚找到工作,老板人很好,只是老板夫人很爱购物。老板忙的时候经常让小明帮忙到商场代为购物。小明很厌烦,但又不好推辞。

    这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折优惠的。
    小明也有个怪癖,不到万不得已,从不刷卡,直接现金搞定。
    现在小明很心烦,请你帮他计算一下,需要从取款机上取多少现金,才能搞定这次购物。

    取款机只能提供100元面额的纸币。小明想尽可能少取些现金,够用就行了。
    你的任务是计算出,小明最少需要取多少现金。

以下是让人头疼的购物单,为了保护隐私,物品名称被隐藏了。
-----------------
****     180.90       88折
****      10.25       65折
****      56.14        9折
****     104.65        9折
****     100.30       88折
****     297.15        半价
****      26.75       65折
****     130.62        半价
****     240.28       58折
****     270.62        8折
****     115.87       88折
****     247.34       95折
****      73.21        9折
****     101.00        半价
****      79.54        半价
****     278.44        7折
****     199.26        半价
****      12.97        9折
****     166.30       78折
****     125.50       58折
****      84.98        9折
****     113.35       68折
****     166.57        半价
****      42.56        9折
****      81.90       95折
****     131.78        8折
****     255.89       78折
****     109.17        9折
****     146.69       68折
****     139.33       65折
****     141.16       78折
****     154.74        8折
****      59.42        8折
****      85.44       68折
****     293.70       88折
****     261.79       65折
****      11.30       88折
****     268.27       58折
****     128.29       88折
****     251.03        8折
****     208.39       75折
****     128.88       75折
****      62.06        9折
****     225.87       75折
****      12.89       75折
****      34.28       75折
****      62.16       58折
****     129.12        半价
****     218.37        半价
****     289.69        8折
--------------------

需要说明的是,88折指的是按标价的88%计算,而8折是按80%计算,余者类推。
特别地,半价是按50%计算。

请提交小明要从取款机上提取的金额,单位是元。
答案是一个整数,类似4300的样子,结尾必然是00,不要填写任何多余的内容。


解题思路:先用word查找替换掉没有信息,处理为有用的信息,之后写程序即可。

     180.90       8.8
      10.25       6.5
      56.14        9
     104.65        9
     100.30       8.8
     297.15        5
      26.75       6.5
     130.62        5
     240.28       5.8
     270.62        8
     115.87       8.8
     247.34       9.5
      73.21        9
     101.00        5
      79.54        5
     278.44        7
     199.26        5
      12.97        9
     166.30       7.8
     125.50       5.8
      84.98        9
     113.35       6.8
     166.57        5
      42.56        9
      81.90       9.5
     131.78        8
     255.89       7.8
     109.17        9
     146.69       6.8
     139.33       6.5
     141.16       7.8
     154.74        8
      59.42        8
      85.44       6.8
     293.70       8.8
     261.79       6.5
      11.30       8.8
     268.27       5.8
     128.29       8.8
     251.03        8
     208.39       7.5
     128.88       7.5
      62.06        9
     225.87       7.5
      12.89       7.5
      34.28       7.5
      62.16       5.8
     129.12        5
     218.37        5
     289.69        8
#include<cstring>
#include<cstdio>
int main()
{
    double ans,a,b;
    ans=0;
    while(scanf("%lf%lf",&a,&b)!=EOF)
    {
        ans+=a*b*0.1;
        printf("%lf",ans);
    }
    return 0;
}

 

2标题:等差素数列


2,3,5,7,11,13,....是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。

2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!

有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:

长度为10的等差素数列,其公差最小值是多少?

注意:需要提交的是一个整数,不要填写任何多余的内容和说明文字。

解题思路:直接暴力枚举即可,跑了两分钟......电脑风扇呼呼地转。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int isprime(int x)
{
    int i;
    for(i=2;i<=sqrt(x);i++)
    {
        if(x%i==0)
        {
            return 0;
        }
    }
    return 1; 
}
int main()
{
    int i,j,k;
    int flag;
    for(k=30;k<=10000;k++)///对公差进行暴力,最小公差就是样例所给的30 
    {
        flag=0;
        for(i=2;i<=1000000;i++) 
        {
            if(isprime(i))///枚举所有的素数 
            {
                for(j=1;j<10;j++)//找该素数之后公差为k的十个数是否也是素数 
                {
                    if(!isprime(i+j*k))
                    {
                        break;
                    }
                }
                if(j==10)
                {
                    flag=1;
                    break;
                }
            }
        }
        if(flag)
        {
            break;
        }
     }
     printf("%d\n",k);
     return 0; 
 } 

 

4标题:方格分割


6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。

如图:p1.png, p2.png, p3.png 就是可行的分割法。


试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。

请提交该整数,不要填写任何多余的内容或说明文字。

解题思路:DFS搜索,这里搜索的是一条分割线。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int vis[7][7];
int ans=0;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int DFS(int x,int y)
{
    int i,x1,y1;
    if(x==0||y==0||x==6||y==6)///搜索到边界,说明形成了分割线 
    {
        ans++;
        return 0;
    }
    for(i=0;i<4;i++)
    {
        x1=x+dir[i][0];
        y1=y+dir[i][1];
        if(x1<0||x1>6||y1<0||y1>6)
        {
            continue;
         } 
        if(!vis[x1][y1])
        {
            vis[x1][y1]=1;
            vis[6-x1][6-y1]=1;
            DFS(x1,y1);
            vis[x1][y1]=0;///回溯撤回标记 
            vis[6-x1][6-y1]=0;
         } 
    }
 }
 int main()
 {
     memset(vis,0,sizeof(vis));
     vis[3][3]=1;
     DFS(3,3);
     printf("%d\n",ans/4);
     return 0;
 }

 

 

5标题:取数位


求1个整数的第k位数字有很多种方法。
以下的方法就是一种。


// 求x用10进制表示时的数位长度
int len(int x){
    if(x<10) return 1;
    return len(x/10)+1;
}
    
// 取x的第k位数字
int f(int x, int k){
    if(len(x)-k==0) return x%10;
    return _____________________;  //填空
}
    
int main()
{
    int x = 23574;
    printf("%d\n", f(x,3));
    return 0;
}

对于题目中的测试数据,应该打印5。

请仔细分析源码,并补充划线部分所缺少的代码。

注意:只提交缺失的代码,不要填写任何已有内容或说明性的文字。

解题思路:这道题一看就是要让我们来补充递归调用的代码,这个题分析f函数,如果x用10进制表示时的数位长度和所求的一样长,就返回个位数,如果不一样长,那么应该截取到一样长,也就是x/10。同时len()函数也是递归实现功能,这也提示了我们。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
// 求x用10进制表示时的数位长度 
int len(int x){
    if(x<10) return 1;
    return len(x/10)+1;
}
    
// 取x的第k位数字
int f(int x, int k){
    if(len(x)-k==0) return x%10;///取最后一位 
    return f(x/10,k);  //填空
}
    
int main()
{
    int x = 23574;
    printf("%d\n", f(x,3));
    return 0;
}

 

6标题:最大公共子串



最大公共子串长度问题就是:
求两个串的所有子串中能够匹配上的最大长度是多少。

比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。

下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。

请分析该解法的思路,并补全划线部分缺失的代码。


#include <stdio.h>
#include <string.h>

#define N 256
int f(const char* s1, const char* s2)
{
    int a[N][N];
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i,j;
    
    memset(a,0,sizeof(int)*N*N);
    int max = 0;
    for(i=1; i<=len1; i++){
        for(j=1; j<=len2; j++){
            if(s1[i-1]==s2[j-1]) {
                a[i][j] = __________________________;  //填空
                if(a[i][j] > max) max = a[i][j];
            }
        }
    }
    
    return max;
}

int main()
{
    printf("%d\n", f("abcdkkk", "baabcdadabc"));
    return 0;
}

 

解题思路:最长公共子序列的模板题,这里附上之前写的博客https://www.cnblogs.com/wkfvawl/p/9362287.html

9标题: 分巧克力


    儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
    小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。

    为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:

    1. 形状是正方形,边长是整数  
    2. 大小相同  

例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。   

输出
输出切出的正方形巧克力最大可能的边长。

样例输入:
2 10  
6 5  
5 6  

样例输出:
2

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


解题思路:我们知道,在1~100000之间的任何一个数x,将各个大块的巧克力按照边长为x的正方形进行切割,如果切割的块数大于等于K,就能够实现每个小朋友都有一份的目标。我们要找的是最大的那个x,而想要找到这个x我们不能暴力,那就需要对这个区间采用二分法来查找。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,k;
struct cho
{
    int h;
    int w;
}c[N];
int judge(int len)
{
    int sum=0;
    int i;
    for(i=0;i<len;i++)
    {
        sum+=(c[i].h/len)*(c[i].w/len);///等分切割蛋糕
        if(sum>=k)///能够分割出来
        {
            return 1;
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&k);
    int low=1;
    int high=100000;
    int mid;
    int i;
    for(i=0;i<n;i++)
    {
        scanf("%d%d",&c[i].h,&c[i].w);
    }
    while(low<high-1)///二分查找最大的
    {
        mid=(low+high)/2;
        if(judge(mid))
        {
            low=mid;///还可以更大
        }
        else
        {
            high=mid;///太大了,不符合目标
        }
    }
    printf("%d\n",mid-1);
    return 0;
}

 

10标题: k倍区间

给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。  

你能求出数列中总共有多少个K倍区间吗?  

输入
-----
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)  

输出
-----
输出一个整数,代表K倍区间的数目。  


例如,
输入:
5 2
1  
2  
3  
4  
5  

程序应该输出:
6

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 2000ms


解题思路:这道题从题意中看出应该使用前缀和但一般的两重循环来限制区间的方法必然会造成时间超限,这里因为有取模运算,实际上是有规律的。计算前缀和然后取余k, 如果前i项和取余k与前j项和取余k后相同,那么i到j这个区间和为k的倍数,因为余数相等,所以这个一定成立(sum[r] - sum[l-1])% k == 0,所以这个区间是符合条件的。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long int
using namespace std;
ll a[100010];
ll cnt[100010];///前缀和取余后i的个数 
ll sum[100010];///存前缀和 
int main()
{   ll ans=0;
    int n,k,i;
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];//构成前缀和 
    }
    for(i=1;i<=n;i++)
    {
        cnt[sum[i]%k]++;
    } 
    for(i=0;i<k;i++)
    {
        if(cnt[i])
        {
            ans+=(cnt[i]*(cnt[i]-1))/2; ///排列组合中的C(n,2) 
        }
    }
    printf("%lld",ans+cnt[0]);
    return 0;
}

 







 

posted @ 2019-03-18 11:24  王陸  阅读(1101)  评论(0编辑  收藏  举报