牛客小白月赛47

传送门

A-牛牛的装球游戏

大致题意:在圆柱体中放半径相同的球,求剩余体积
解题思路:只要圆柱体的高度h对球的直径2*r取模就可以求得球的个数n,圆柱体体积减去n个球的体积即为剩余体积
注意细节:π的取值已给出,输出结果保留三位小数

coding
#include<iostream>
#include<iomanip>
using namespace std;

#define PI 3.141592653589

int main()
{
    int T;
    cin>>T;
    while(T--){
        int r,h;
        cin>>r>>h;
        //计算球和圆柱体的体积
        double s1=4/3.0*PI*r*r*r,s2=PI*r*r*h;
        //计算最多可以放入的球的个数
        int n=h/(r*2);
        cout<<fixed<<setprecision(3);//保留三位小数
        cout<<s2-n*s1<<endl;
    }
    return 0;
}

B-牛牛的数字集合

大致题意:给定n个数,划分为m个集合,如何划分使得价值和最小,集合价值:集合中数的乘积的m次方,结果对1e9+7取模
解题思路:不难发现,划分越多,价值和就会越大,最佳选择就是不划分,直接就一个集合。
思路证明:写不出来,不知道怎么表达意思
注意细节:数的范围和个数都在1e6以内,所有数相乘可能会爆,每次相乘时都要取模

coding
#include<iostream>
using namespace std;

typedef long long LL;
const int MOD=1e9+7;

int main()
{
    int n;
    cin>>n;
    LL res=1;
    for(int i=0;i<n;i++){
        int x;
        cin>>x;
        res=res*x%MOD;
    }
    cout<<res;
    return 0;
}

C-小猫排队

大致题意:小猫啾啾排在n只猫咪后,她每隔一分钟可以施展魔法(与前面距离最近且可爱值大于她的猫咪交换位置),先施展魔法然后排最前面的离开,计算需要几分钟才能离开
解题思路:使用双端队列deque模拟施法及移动过程,由于只会向前移动,所以直接将换到后面的猫咪弹出队列即可
注意细节:当前面没有可以换的猫咪时应该按顺序排,但我们已经将他们弹出队列,所以在弹他们时要记录个数,比赛期间就是因为这个卡住了-__-;注意将离开的队头弹出

coding
#include<iostream>
#include<deque>
using namespace std;

int main()
{
    int n;
    cin>>n;
    deque<int> q;//队列
    while(n--){
        int x;
        cin>>x;
        q.push_back(x);
    }
    int x;//啾啾的可爱值
    cin>>x;
    int res=0;
    while(true){
        int b=0;//记录前面有几个小于等于啾啾的
        //由于只会向前交换,所以不需要考虑换到后面的猫咪,所以直接弹出队列
        while(q.size()&&q.back()<=x){
            q.pop_back();
            b++;
        }
        //如果没有能换的,那就按顺序走
        if(q.empty()){
            res+=(b+1);
            break;
        }
        q.pop_back();//和大于啾啾的猫咪交换位置
        res++;
        if(q.empty())    break;
        q.pop_front();//队头离开
    }
    cout<<res;
    return 0;
}

D-造桥


大致题意:n个字符串首尾连接(当前字符串尾部和下一字符串首部相同则可连接),得到一个尽可能长的字符串
解题思路:线性dp,每个字符串有取和不取两种状态;设字符串开头、结尾和长度为x,y,m;状态转移方程为f[i][y]=max(f[i-1][y],f[i-1][x]+m);分别对应取和不取两种状态,其中状态之间互不影响,所以第一维状态可省略
注意细节:多组数据,所以需要将数组清零;size()length()返回无符号整数

coding
#include<iostream>
#include<cstring>
using namespace std;

int f[26];

int main()
{
    int T;
    cin>>T;
    while(T--){
        memset(f,0,sizeof f);
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            string s;
            cin>>s;
            int m=s.size();
            f[s[m-1]-'a']=max(f[s[m-1]-'a'],f[s[0]-'a']+m);
        }
        int res=0;
        for(int i=0;i<26;i++)
            res=max(res,f[i]);
        cout<<res<<endl;
    }
    return 0;
}

E-牛牛的方格图

大致题意:给定一个n*m的矩阵,每个格子有一种颜色;两个相同颜色的点矩形之间视为被覆盖,求有多少个未被覆盖的点
思路:存储每种颜色横纵坐标的最大最小值(左上角和右下角),用差分矩阵(作用:给连续区间同时加上一个数)的方式上标记,标记为0的就是没有被覆盖的点
注意细节:输入超过1e5,最好使用scanf输入

coding
#include<iostream>
#include<cstdio>
using namespace std;

const int N=1010,M=1000010;
int a[M][4];//每个颜色的横纵坐标的最大最小值
int sum[N][N];//前缀和

int main()
{
    for(int i=1;i<=1e6;i++)
        a[i][1]=a[i][3]=1e4;//横纵坐标最小值赋初值
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            int x;//颜色
            scanf("%d",&x);
            a[x][0]=max(a[x][0],i);
            a[x][1]=min(a[x][1],i);
            a[x][2]=max(a[x][2],j);
            a[x][3]=min(a[x][3],j);
        }
    for(int i=1;i<=1e6;i++){//枚举每种颜色
        if(a[i][0]==0)    continue;//没有这种颜色
        if(a[i][0]==a[i][1]&&a[i][2]==a[i][3])    continue;//最大值=最小值,说明只有一种颜色,无法覆盖
        //差分矩阵
        sum[a[i][0]+1][a[i][2]+1]++;//左上角
        sum[a[i][0]+1][a[i][3]]--;//右上角
        sum[a[i][1]][a[i][2]+1]--;//左下角
        sum[a[i][1]][a[i][3]]++;//右下角
    }
    //求前缀和
    int res=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
            if(sum[i][j]==0)    res++;//没有被覆盖
        }
    cout<<res;
    return 0;
}

F-缆车

posted @ 2022-04-10 20:32  林汐岚  阅读(117)  评论(0)    收藏  举报