蓝桥杯4道例题(3)

难度由低到高
1.移动距离
来源:第六届蓝桥杯省赛C++B组
题目链接
在这里插入图片描述
题解:怎么数最短应该就不用说了,直接说怎么算,有两种情况,第一种,2个楼号所在的行递增方向是相同的,这种情况比较简单,分别计算出他们在第几行,然后计算出2者差的绝对值x(竖直方向的距离),然后通过x计算出与较小的数同列与较大的数同行的数(p),然后用这个数与较大的数算出行方向的距离,二者之和就是其最大值;第二种情况因为行递增方向不同,那么我们找出较小的数正下方的数,情况就与第一种情况相同了,不过此时计算p时,要用x-1;

c++代码:

#include<iostream>
#include<math.h>
using namespace std;

int main()
{

int w,m,n;
cin>>w>>m>>n;
if(m>n)
{
    int t=n;
    n=m;
    m=t;
}
int min,max;
if(n%w==0)//左右2端
    {
      max=n/w;
        
    }
else
{
    max=n/w+1;
}
if(m%w==0)//左右2端
    {
      min=m/w;
        
    }
else
{
    min=m/w+1;
}
int ans=max-min;

if(min%2==max%2)
cout<<ans+abs(n-m-ans*w);
else
{   int x=min*w-m;
    m=x*2+1+m;
    cout<<ans+abs(n-m-(ans-1)*w);
}

}

java:

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
   

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int w,m,n;
        w=sc.nextInt();
        m=sc.nextInt();
        n=sc.nextInt();
        if(m>n)
        {
            int t=n;
            n=m;
            m=t;
        }
        int min,max;
        if(n%w==0)//左右2端
        {
            max=n/w;

        }
        else
        {
            max=n/w+1;
        }
        if(m%w==0)//左右2端
        {
            min=m/w;

        }
        else
        {
            min=m/w+1;
        }
        int ans=max-min;

        if(min%2==max%2)
            System.out.println(ans+Math.abs(n-m-ans*w));
        else
        {   int x=min*w-m;
            m=x*2+1+m;
            System.out.println(ans+Math.abs(n-m-(ans-1)*w));
        }

    }
    
    }

2.蚂蚁感冒
来源:第五届蓝桥杯省赛C++A/B组
题目链接
在这里插入图片描述
题解:这题看似很难是因为碰头这事很难处理,但我们这样看,不管是那种情况的碰头,碰头的结果都是2只一样的蚂蚁朝反方向走去,那么我们就可以把碰头等效于穿过,这样题目就好理解多了;现在我们分情况看;
1.病蚂蚁的前方有和蚂蚁方向相反的蚂蚁,此时的答案是病蚂蚁后方和病蚂蚁方向相同的蚂蚁加上病蚂蚁前方和病蚂蚁方向相反的蚂蚁再加1
2.如果蚂蚁前方没有和蚂蚁方向相反的蚂蚁,则结果为1
c++代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n; 
int main()
{
    cin>>n;
    int pivot, left = 0, right = 0;
    cin>>pivot;

    for (int i = 1; i < n; i++)
    {
        int x;
        scanf("%d", &x);
        //找到感冒蚂蚁左边边且向右走的
        if (abs(x) < abs(pivot) && x > 0) right++;
        //找到感冒蚂蚁右边且向左走的
        if (abs(x) > abs(pivot) && x < 0) left++;
    }
    //特殊情况
    if ((pivot < 0 && right == 0) || pivot > 0 && left == 0) puts("1");//判断感冒蚂蚁正前方是否有和感冒蚂蚁方向的蚂蚁
    else printf("%d\n", left + right + 1);

    return 0;
}


java代码:

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

public class Main {


    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n;
        n=sc.nextInt();
        int pivot, left = 0, right = 0;
        pivot=sc.nextInt();

        for (int i = 1; i < n; i++)
        {
            int x;
            x=sc.nextInt();
            //找到感冒蚂蚁左边边且向右走的
            if (Math.abs(x) <Math. abs(pivot) && x > 0) right++;
            //找到感冒蚂蚁右边且向左走的
            if (Math.abs(x) > Math.abs(pivot) && x < 0) left++;
        }
        //特殊情况
        if ((pivot < 0 && right == 0) || pivot > 0 && left == 0) System.out.println(1);//判断感冒蚂蚁正前方是否有和感冒蚂蚁方向的蚂蚁
        else System.out.println(left+right+1);

    }

    }

3.带分数
来源:第四届蓝桥杯省赛C++B组
题目链接

在这里插入图片描述
题解:
从1到9一共9个数,题目要求不从不漏的出现,我们假设整数是a,分子是b,分母是c,把他们放在一起组成abc这样一个排列,因为不从不漏,这个排列一定包含在1到9的全排列之中,那么我们把1到9的全排列全部找不来,然后把这个全排列分成3段,要分成三段,除去这个排列的开头和结尾,还需要枚举2个分割点,然后分别计算每一段数,带入看看是否符合题目要求即可;

代码:
c++

#include<iostream>

using namespace std;
int num[10];
bool used[10];
int ans;
int n;
int calc(int l, int r){//计算
    int res = 0;
    for(int i = l; i <= r; i++)
        res = res * 10 + num[i];
    return res;
}

void dfs(int v)
{


    if(v==10)//当v=10时,num数组里存在了一个全排列了
    for(int i=1;i<8;i++)
        for(int j=i+1;j<9;j++)
        {

            int a = calc(1, i);
                int b = calc(i + 1, j);
                int c = calc(j + 1, 9);
                //注意判断条件,因为C++中除法是整除,所以要转化为加减乘来计算
                if(a * c + b == c * n) ans++;
        }
    for(int i = 1; i <= 9; i++)//递归找全排列
        if(!used[i]){
            used[i] = true; //标记使用,防止后面使用
            num[v] = i;
            dfs(v + 1);
            used[i] = false; //回溯回来取消对此数字的标记
        }

}

int main()
{

cin>>n;


dfs(1);


cout<<n<<" "<<ans<<endl;

}

java代码:

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
   static int num[]=new int[10];
   static boolean used[]=new boolean[10];
   static int n;
   static int ans=0;

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        dfs(1);
        System.out.println(n+" "+ans);

    }
    static int calc(int l, int r){//计算
        int res = 0;
        for(int i = l; i <= r; i++)
            res = res * 10 + num[i];
        return res;
    }
    
    
   static  void dfs(int v)
    {


        if(v==10)//当v=10时,num数组里存在了一个全排列了
            for(int i=1;i<8;i++)
                for(int j=i+1;j<9;j++)
                {

                    int a = calc(1, i);
                    int b = calc(i + 1, j);
                    int c = calc(j + 1, 9);
                    //注意判断条件,因为C++中除法是整除,所以要转化为加减乘来计算
                    if(a * c + b == c * n) ans++;
                }
        for(int i = 1; i <= 9; i++)//递归找全排列
            if(!used[i]){
                used[i] = true; //标记使用,防止后面使用
                num[v] = i;
                dfs(v + 1);
                used[i] = false; //回溯回来取消对此数字的标记
            }

    }


}

4.密码脱落
来源:第七届蓝桥杯省赛C++A组
题目链接
在这里插入图片描述
题解:这题我们不要想着添加,我们这样想,我们求这个序列再最小掉落多少个可以成为回文序列,那么掉落的数量就是最后的答案,因为只要把这些掉落添加到原序列的某个位置得到的一定是回文序列;
那么这题就变成了一个区间dp的问题
c++代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>

using namespace std;

const int N = 1010;

char s[N];
int f[N][N];//区间 [l, r] 中所有子序列的最大长度

int main(){
    cin>>s;

    int n = strlen(s);

    for(int len = 1; len <= n; len ++){//分别枚举序列长度
        for(int l = 0; l + len - 1 < n; l ++){
            int r = l + len - 1;
            if(len == 1) f[l][r] = 1;
            else{
                if(s[l] == s[r]) f[l][r] = f[l + 1][r - 1] + 2;//加上s[l]和s[r]可以构成回文序列
                f[l][r] = max(f[l][r], f[l][r - 1]);
                f[l][r] = max(f[l][r], f[l + 1][r]);//为什么不考虑f[l + 1][r - 1],因为它的值一定小于等于f[l][r - 1]||f[l + 1][r]
            }
        }
    }

    cout<<n-f[0][n-1];

    return 0;
}


java代码:

import java.util.Scanner;

public class Main {
    static int N = 1010;
    static int[][] f = new int[N][N];
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String s = scan.next();
        int n = s.length();
        for(int len = 1;len <= n;len ++)
        {
            for(int i = 0;i + len - 1 < n;i ++)
            {
                int j = i + len - 1;

                if(len == 1) f[i][j] = 1;
                else
                {
                    f[i][j] = Math.max(f[i][j - 1], f[i + 1][j]);
                    if(s.charAt(i) == s.charAt(j)) f[i][j] = Math.max(f[i][j], f[i + 1][j - 1] + 2);
                }
            }
        }
        System.out.println(n - f[0][n - 1]);
    }
}


  • 59
    点赞
  • 216
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值