HNUCM2022年天梯赛选拔赛第1场

A: 颜色叠加

题目描述

热爱科学的Kimi这段时间在研究各种颜色,今天他打算做一个关于颜色叠加的小实验。
Kimi有很多张蓝色和黄色的长方形透明塑料卡片。众所周知,如果把蓝色和黄色混合在一起就会变成绿色。因此,Kimi对着光观察蓝色透明卡片和黄色透明卡片的叠加部分也就可以看到绿色啦。
假设在一个二维平面中,一张蓝色的透明卡片和一张黄色的透明卡片都与坐标轴平行放置,即卡片的横边与X轴平行,竖边与Y轴平行。
现在给出一张蓝色卡片和一张黄色卡片的左上角坐标(均为整数)以及两张卡片的长和宽(均为正整数)。
【注意:此处定义与X轴平行的那组边为长边,与Y轴平行的那组边为宽边】
请编写一个程序计算这两张卡片叠加后所形成的绿色区域的面积。

输入

单组输入。
第1行输入四个整数,分别表示蓝色长方形透明卡片的左上角坐标(X坐标和Y坐标)、长和宽。两两之间用英文空格隔开。
第2行输入四个整数,分别表示黄色长方形透明卡片的左上角坐标(X坐标和Y坐标)、长和宽。两两之间用英文空格隔开。
两张长方形透明卡片的X坐标和Y坐标的取值范围为[-1000, 1000],长和宽的取值范围为[1,200]。

输出

输出一个非负整数,表示两张卡片叠加后所形成的绿色区域的面积。

  • 思维题
  • 画个图自己推公式就行,我这不是最简做法
  • 给出下图,可以参考我的做法
  • 在这里插入图片描述
#include <bits/stdc++.h>
using namespace std;

int main(){

    int x,y,xx,yy,a,b,aa,bb;
    int i,j;
    scanf("%d%d%d%d",&x,&y,&a,&b);
    scanf("%d%d%d%d",&xx,&yy,&aa,&bb);
    int dx=abs(x-xx);
    int dy=abs(y-yy);
    if(x<=xx){
        i=min(aa,a-dx);
    }
    else i=min(a,aa-dx);
    if(y>=yy){
        j=min(bb,b-dy);
    }
    else j=min(b,bb-dy);
    if(i<=0||j<=0)puts("0");
    else printf("%d\n",i*j);
    return 0;
}

B: 勤劳的老杨

题目描述

勤劳的老杨最近收到了一个任务清单,在这个清单上有N项不同的工作任务。对于每一项任务都给出了两个时间[X, Y],其中X表示任务的起始时间(任务从第X天开始,包含第X天),Y表示任务的结束时间(任务到第Y天结束,包含第Y天)。
认真的老杨对待每一项任务都是一心一意的。一旦他决定做某一项任务,在该任务没有完成之前他不会同时再做另一项任务,也就是说在任意时刻老杨手头最多只有一项任务。
假设完成每一项任务所获得的报酬都是相等的。那么,老杨应该如何来安排自己的时间才可以得到最多的报酬呢?
请你编写一个程序帮老杨计算出他最多可以完成的任务数量。保证至少能完成一项任务。

输入

单组输入。
第1行输入一个正整数N表示任务清单上任务的总数。(N<=1000)
第2行到第N行每一行包含两个正整数,分别表示每一项任务的开始时间和结束时间,两个正整数之间用空格隔开。

输出

输出老杨最多可以完成的任务数量。

  • 贪心
#include<bits/stdc++.h>
using namespace std;
struct node
{
    int a;
    int b;
}ans[1005];
bool cmp(const node q,const node p){
    return q.b<p.b;
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>ans[i].a;
        cin>>ans[i].b;
    }
    sort(ans,ans+n,cmp);//按结束时间从小到大大排序
    int cou=0;
    int end=-1;
    for(int i=0;i<n;i++)
    if(ans[i].a>end){//注意时间不能重叠
           cou++;
           end=ans[i].b;
       }
       cout<<cou<<endl;
    return 0;
}

C: 秘密大厦的访客

题目描述

Kimi最近在负责一栋秘密大厦的安保工作,他的工作是记录大厦的来访者情况。
每个来访者都有一个与之对应的唯一编号,在每一条到访记录中记录了该来访者的编号。
现在Kimi需要统计每一条记录中的来访者是第几次光临秘密大厦。

输入

单组输入,每组两行。
第1行包含一个正整数n,表示记录的条数,n不超过1000;
第2行包含n个正整数,依次表示Kimi的记录中每位来访者的编号,两两之间用空格隔开。

输出

输出1行,包含n个正整数,两两之间用空格隔开,依次表示每条记录中的来访者编号是第几次出现。

  • 签到题
  • 直接模拟,做法很多
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
    int n,m;
    scanf("%d",&n);
    map<int,int>mp;
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        mp[m]++;
        printf("%d%c",mp[m],i==n ? '\n':' ');
    }
    return 0;
}

D: 最大能量

题目描述

一年一度的宇宙超级运动会在宇宙奥特英雄体育场隆重举行。X星人为这场运动会准备了很长时间,他大显身手的时刻终于到了!
为了保持良好的竞技状态和充沛的体能,X星人准备了N种不同的能量包。
虽然每种能量包都有无限个,但是因为同一种能量包使用太多会带来副作用,因此同样的能量包不能同时使用超过两个,也就是说最多同时可以使用两个相同的能量包。
每种能量包都有一个重量值和能量值。由于这些能量包的特殊性,必须要完整地使用一个能量包才能够发挥功效,否则将失去对应的能量值。
考虑到竞赛的公平性,竞赛组委会规定每个人赛前补充的能量包的总重量不能超过W。
现在需要你编写一个程序计算出X星人能够拥有的最大能量值是多少?

输入

单组输入。
第1行包含两个正整数N和W,其中N<=10^ 3,W<=10^ 3。
第2行到第N+1行,每一行包含两个正整数,分别表示每一种能量包的重量和能量值,两个正整数之间用空格隔开。每一种能量包的重量和能量值都是小于等于100的正整数。

输出

输出X星人能够拥有的最大能量值。

  • 背包
  • 可以看成每个物品个数为2的多重背包,用多重背包的方法做;也可以看成总共有2*n个物品,用一般背包的方法做
//方法1
#include <bits/stdc++.h>
using namespace std;
int c[1005],w[1005];//重量 能量
int f[10005];
int main(){

    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>c[i]>>w[i];
    for(int i=1;i<=n;i++)
    for(int j=m;j>=c[i];--j){
        for(int k=1;k<=2&&k*c[i]<=j;k++){
            f[j]=max(f[j],f[j-c[i]*k]+w[i]*k);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}
//方法2
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int a[2*N],b[2*N],dp[N],n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        a[i+n]=a[i],b[i+n]=b[i];
    }
    for(int i=1;i<=2*n;i++){
        for(int j=m;j>=a[i];j--){
            dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
        }
    }
    cout<<dp[m]<<'\n';
    return 0;
}

E: 最大素数

题目描述

输入一个数字字符串,从中删除若干个(包含0个)数字后可以得到一个素数,请编写一个程序求解删除部分数字之后能够得到的最大素数。
例如,输入“1234”,删除1和4,可以得到的最大素数为23。

输入

输入一个数字字符串,从中删除若干个(包含0个)数字后可以得到一个素数,请编写一个程序求解删除部分数字之后能够得到的最大素数。
例如,输入“1234”,删除1和4,可以得到的最大素数为23。

输出

输入一个数字字符串,从中删除若干个(包含0个)数字后可以得到一个素数,请编写一个程序求解删除部分数字之后能够得到的最大素数。
例如,输入“1234”,删除1和4,可以得到的最大素数为23。

  • 搜索
  • 这里用的bfs,优先搜索当前最大的数,如果这个数已经是素数那么就是答案
  • 我说不清楚,参考代码吧
#include <bits/stdc++.h>
using namespace std;
bool isprime(int n){//素数判断
    if(n<2)return 0;
    for(int i=2;i<=(int)sqrt(n);++i)
        if(n%i==0)return 0;
    return 1;
}
struct node {
    string s;
    int len;
    bool operator<(const node &q)const{
        if(len!=q.len)return len<q.len;
        return s<q.s;
    }
};
bool check(string str){
    int m=0;
    for(int i=0;i<str.size();i++){
        m=m*10+str[i]-'0';
    }
    return isprime(m);
}
bool flag;
map<string,bool>vis;
string s;
void bfs(){
    priority_queue<node>q;
    q.push({s,s.size()});
    while(!q.empty()){
        node k=q.top();
        q.pop();
        if(vis[k.s])continue;
        vis[k.s]=1;
        if(check(k.s)){
            cout<<k.s<<endl;
            flag=1;
            return ;
        }
        for(int i=0;i<k.s.size();i++){//去掉第i个字符
            string s1=k.s.substr(0,i)+k.s.substr(i+1);
            q.push({s1,s1.size()});
        }
    }
}
int main(){

    cin>>s;
    bfs();
    if(!flag)puts("No result.");
    return 0;
}

F: 最大计分

题目描述

小米和小花在玩一个删除数字的游戏。
游戏规则如下:
首先随机写下N个正整数,然后任选一个数字作为起始点,从起始点开始从左往右每次可以删除一个数字,但是必须满足下一个删除的数字要小于上一个删除的数字。每成功删除一个数字计1分。
请问对于给定的N个正整数,一局游戏过后可以得到的最大计分是多少?

输入

单组输入。
第1行输入一个正整数N表示数字的个数(N<=10^3)。
第2行输入N个正整数,两两之间用空格隔开。

输出

对于给定的N个正整数,一局游戏过后可以得到的最大计分值。

  • 最长下降子序列
  • 将数组逆转就等价于求最长上升子序列长度
#include <bits/stdc++.h>
using namespace std;
int arr[1005];
int main(){
 
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>arr[i];
    reverse(arr,arr+n);
    vector<int>stk;
    stk.push_back(arr[0]);
    for (int i = 1; i < n; ++i) {
        if (arr[i] > stk.back())
            stk.push_back(arr[i]);
        else
            *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i];
    }
    cout << stk.size() << endl;
    return 0;
}

G: 密钥

题目描述

X星人又截获了Y星人的一段密文。
破解这段密文需要使用一个密钥,而这个密钥存在于一个正整数N中。
聪明的X星人终于找到了获取密钥的方法:这个正整数的最后一位是一个非零数K(K>=2),需要将正整数N切分成K个小的整数,并且要使得这K个较小整数的乘积达到最大。而所得到的最大乘积就是破解密文所需的密钥。
你能否帮X星人编写一段程序来得到密钥呢?

输入

X星人又截获了Y星人的一段密文。
破解这段密文需要使用一个密钥,而这个密钥存在于一个正整数N中。
聪明的X星人终于找到了获取密钥的方法:这个正整数的最后一位是一个非零数K(K>=2),需要将正整数N切分成K个小的整数,并且要使得这K个较小整数的乘积达到最大。而所得到的最大乘积就是破解密文所需的密钥。
你能否帮X星人编写一段程序来得到密钥呢?

输出

将N划分为K个整数后的最大乘积。

  • 搜索
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
ll ans;

void dfs(ll sum,ll m,int res){
    if(res==1){
        ans=max(ans,sum*m);
        return ;
    }
    int num=(int)log10(m)+1;//m的位数
    int k=10;
    for(int i=1;i<=num-res+1;i++){//保证剩余的数至少还有res-1位
        dfs(sum*(m%k),m/k,res-1);
        k*=10;
    }
    return ;
}
int main(){

    cin>>n;
    dfs(1ll,n,n%10);
    cout<<ans<<endl;
    return 0;
}

H: X星大学

题目描述

X星大学新校区终于建成啦!
新校区一共有N栋教学楼和办公楼。现在需要用光纤把这N栋连接起来,保证任意两栋楼之间都有一条有线网络通讯链路。
已知任意两栋楼之间的直线距离(单位:千米)。为了降低成本,要求两栋楼之间都用直线光纤连接。
光纤的单位成本C已知(单位:X星币/千米),请问最少需要多少X星币才能保证任意两栋楼之间都有光纤直接或者间接相连?
注意:如果1号楼和2号楼相连,2号楼和3号楼相连,则1号楼和3号楼间接相连。

输入

单组输入。
第1行输入两个正整数N和C,分别表示楼栋的数量和光纤的单位成本(单位:X星币/千米),N<=100,C<=100。两者之间用英文空格隔开。
接下来N*(N-1)/2行,每行包含三个正整数,第1个正整数和第2个正整数表示楼栋的编号(从1开始一直到N),编号小的在前,编号大的在后,第3个正整数为两栋楼之间的直线距离(单位:千米)。

输出

输出最少需要多少X星币才能保证任意两栋楼之间都有光纤直接或者间接相连。

  • 最小生成树模板题
//prim()最小生成树
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
int n,c;
int dist[105];
bool vis[105];
int a[105][105];
ll prim(int pos){
    memset(dist,INF,sizeof(dist));
    dist[pos]=0;
    ll sum=0;
    for(int i=1;i<=n;i++){
        int cur=-1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&(cur==-1||dist[j]<dist[cur]))
                cur=j;
        }
    if(dist[cur]>=INF)return INF;
    sum+=dist[cur];
    vis[cur]=1;
    for(int l=1;l<=n;l++)
        if(!vis[l])dist[l]=min(dist[l],a[cur][l]);
    }
    return sum;
}
int main() {
    scanf("%d%d",&n,&c);
    int x,y,z;
    memset(a,INF,sizeof(a));
    for(int i=1;i<=n;i++)a[i][i]=0;
    for(int i=1;i<=n*(n-1)/2;i++){
        scanf("%d%d%d",&x,&y,&z);
        a[x][y]=min(a[x][y],z);
        a[y][x]=a[x][y];
    }
    printf("%lld\n",prim(1)*c);
    return 0;
}
//Kruskal()最小生成树
#include<bits/stdc++.h>
using namespace std;
struct node {
	int x,y,z;
}edge[10005];
bool cmp(node a,node b) {
	return a.z < b.z;
}
int fa[105];
int n,m,c;
long long sum;
int get(int x) {
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}

int main() {
    scanf("%d%d",&n,&c);
    m=n*(n-1)/2;
    for(int i = 1; i <= m; i ++) {
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    }
    for(int i = 0; i <= n; i ++) {
        fa[i] = i;
    }
    sort(edge + 1,edge + 1 + m,cmp);
    // 每次加入一条最短的边
    for(int i = 1; i <= m; i ++) {
        int x = get(edge[i].x);
        int y = get(edge[i].y);
        if(x == y) continue;
        fa[y] = x;
        sum += edge[i].z;
    }
    printf("%lld\n",sum*c);
    return 0;
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值