蓝桥杯刷题二

6.费解的开关

这题就是上一节的那题的扩展版了

首先第一行的状态如果确定了 那么后面要进行的操作也是确定的 所以枚举一下第一行的状态就好了

但是不同的是 上一题的第一个状态是确定的 但是这题的第一个状态不是确定的 因为可以通过按很多不同的按钮使得第一个的状态改变 而上一题只能有一个操作使得第一个状态的改变 所以这个还需要加一个二进制枚举一下第一行的状态 然后操作后面的按钮使得第一行全部变为符合要求的状态 再判断一下合法解

#include<bits/stdc++.h>
using namespace std;
const int N=6;
char str[N][N],temp[N][N];
int dx[6]={-1,0,1,0,0},dy[6]={0,1,0,-1,0};//上 右 下 左 中
void trans(int x,int y)//用来改变一个位置
{
    for(int i=0;i<5;i++)//枚举五个方向
    {
        int tx=x+dx[i],ty=y+dy[i];
        if(tx<0||tx>4||ty<0||ty>4) continue;
        temp[tx][ty]^=1;
    }
}
void solve()
{
    for(int i=0;i<5;i++) scanf("%s",str[i]);
    int ans=7;
    for(int i=0;i<32;i++)
    {
        int cnt=0;
        memcpy(temp,str,sizeof str);
        for(int j=0;j<5;j++)//枚举改第一行的状态
            if(i>>j&1) cnt++,trans(0,j);
        for(int j=0;j<4;j++)//枚举改剩下的几行
          for(int k=0;k<5;k++)
              if(temp[j][k]=='0') cnt++,trans(j+1,k);
        for(int j=0;j<5;j++)//看看最后几行是不是全为1
           if(temp[4][j]=='0') cnt=0x3f3f3f3f;
        ans=min(cnt,ans);
    }
    if(ans<=6) printf("%d\n",ans);//假如小于6了
    else puts("-1");
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}

7.带分数

朴素思路:

1.首先用全排列枚举1到9的所有方案

2.枚举a b c的位数 (n=a+b/c)

3.判断合不合法

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 10;
int cnt=0;
int num[9]={1,2,3,4,5,6,7,8,9};

int cal(int i,int j)
{
    int res=0;
    for(int k = i+1 ; k<=j ; k++)
    {
        res=res*10+num[k];
    }
    return res;
}


// 设target = a+b/c,
int main()
{
    int target;
    cin>>target;
    // 枚举1~9的所有情况
    do
    {
        for(int i = 0 ; i<9 ; i++)          // 二重循环枚举组合
        {
            for(int j = i+1 ; j<9 ; j++)
            {
                int a = cal(-1,i);
                int b = cal(i,j);
                int c = cal(j,8);
                if(target*c==a*c+b)cnt++;       // 注意,由于c++是整除运算,因此我们用乘法来替代除法
            }
        }
    }while(next_permutation(num,num+9));
    cout<<cnt<<endl;
    return 0;
}

优化做法:

首先n的在上面的做法是没用过的 因为是在枚举a b c 那么是不是就可以枚举 a b (n=a+b/c)  =>      ( b=n*c - a*c ) 然后通过这个式子就可以直接算出 b 了 就不用枚举了 那么我们在dfs a 选什么的时候 再 dfs c 选什么 再判断一下b合不合法就行了

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

using namespace std;

const int N = 10;

int n;
bool st[N], backup[N];
int ans;

bool check(int a, int c)
{
    long long b = n * (long long)c - a * c;//这个可能会溢出

    if (!a || !b || !c) return false;

    memcpy(backup, st, sizeof st);
    while (b)
    {
        int x = b % 10;     // 取个位
        b /= 10;    // 个位删掉
        if (!x || backup[x]) return false;
        backup[x] = true;
    }

    for (int i = 1; i <= 9; i ++ )
        if (!backup[i])
            return false;

    return true;
}

void dfs_c(int u, int a, int c)
{
    if (u > 9) return;

    if (check(a, c)) ans ++ ;

    for (int i = 1; i <= 9; i ++ )
        if (!st[i])
        {
            st[i] = true;
            dfs_c(u + 1, a, c * 10 + i);
            st[i] = false;
        }
}

void dfs_a(int u, int a)
{
    if (a >= n) return; //剪枝 a如果>=n 一定不是合法的
    if (a) dfs_c(u, a, 0);//如果a不为0是才去枚举c 一种剪枝

    for (int i = 1; i <= 9; i ++ )
        if (!st[i])
        {
            st[i] = true;
            dfs_a(u + 1, a * 10 + i);
            st[i] = false;
        }
}

int main()
{
    cin >> n;

    dfs_a(0, 0);//选了0个数 a的取值为0

    cout << ans << endl;

    return 0;
}

8.飞行员兄弟

这题又和费解的开关是同一类题 但是这题没有费解的开关那种性质了 按第一行并不能确定所有状态 所以我们直接二进制暴力枚举4*4的棋盘怎么按 就行了

#include <bits/stdc++.h>
using namespace std;
char g[7][7];
char backup[7][7];
int get(int a,int b)
{
    return 4*a+b;
}
void turn(int a,int b)
{
    if(g[a][b]=='+') g[a][b]='-';
    else g[a][b]='+';
}
int main()
{
    for(int i=0;i<4;i++)
        cin>>g[i];
    vector<pair<int,int>>res;
    for(int i=0;i<1<<16;i++)
    {
        vector<pair<int,int>> t;
        memcpy(backup,g,sizeof g);
        for(int j=0;j<4;j++)
            for(int k=0;k<4;k++)
            {
                if(i>>get(j,k)&1)
                {
                    t.push_back({j,k});
                    for(int l=0;l<4;l++)
                        turn(j,l),turn(l,k);
                    turn(j,k);
                }
            }
        int fl=false;
        for(int j=0;j<4;j++)
            for(int k=0;k<4;k++)
                if(g[j][k]=='+')
                    fl=true;
        if(!fl&&(!res.size()||res.size()>t.size()))
            res=t;
        memcpy(g,backup,sizeof backup);
    }
    cout<<res.size()<<endl;
    for(int i=0;i<res.size();i++)
    {
        cout<<res[i].first+1<<" "<<res[i].second+1<<endl;
    }
    return 0;
}

9.数的范围

二分模板题

我的总结 二分左右边界的时候不要想着模板 我个人觉得是真的乱

二分的时候

首先要自己自己二分的是左边界还是右边界

然后再去将没有等号的判断条件放到你不需要的区间那里

比如下面这段代码 我需要的是右边界 所以是左区间 那么我就把+1 -1 扔到这里右区间

while(l<r)
{
    int mid=l+r>>1;
    if(a[mid]>=x) r=mid;
    else l=mid+1;

}

另外一个同理

while(l<r)
{
    int mid=l+r+1>>1;
    if(a[mid]>x) r=mid-1;
     else l=mid;
}

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    while(q--)
    {
        int x;
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r)
        {
            int mid=l+r>>1;
            if(a[mid]>=x) r=mid;
            else l=mid+1;

        }
        if(a[l]==x)
        {
            printf("%d ",l);
            l=0,r=n-1;
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(a[mid]>x) r=mid-1;
                else l=mid;
            }
            printf("%d\n",l);
        }
        else printf("-1 -1\n");
    }
    return 0;
}

10.数的三次根

这题也是二分逼近答案就好了

浮点二分还比较简单 不用考虑边界问题

#include <bits/stdc++.h>
using namespace std;
const double eps=1e-8;
int main()
{
    double n;
    scanf("%lf",&n);
    double l=-10000,r=10000;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(mid*mid*mid>=n) r=mid;
        else l=mid;
    }
    printf("%lf\n",l);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值