0
点赞
收藏
分享

微信扫一扫

HNUCM天梯赛选拔赛第一场

月半小夜曲_ 2022-03-11 阅读 61

题目

A:颜色叠加

题目描述
热爱科学的Kimi这段时间在研究各种颜色,今天他打算做一个关于颜色叠加的小实验。
Kimi有很多张蓝色和黄色的长方形透明塑料卡片。众所周知,如果把蓝色和黄色混合在一起就会变成绿色。因此,Kimi对着光观察蓝色透明卡片和黄色透明卡片的叠加部分也就可以看到绿色啦。
假设在一个二维平面中,一张蓝色的透明卡片和一张黄色的透明卡片都与坐标轴平行放置,即卡片的横边与X轴平行,竖边与Y轴平行。
现在给出一张蓝色卡片和一张黄色卡片的左上角坐标(均为整数)以及两张卡片的长和宽(均为正整数)。
【注意:此处定义与X轴平行的那组边为长边,与Y轴平行的那组边为宽边】
请编写一个程序计算这两张卡片叠加后所形成的绿色区域的面积。
输入
单组输入。
第1行输入四个整数,分别表示蓝色长方形透明卡片的左上角坐标(X坐标和Y坐标)、长和宽。两两之间用英文空格隔开。
第2行输入四个整数,分别表示黄色长方形透明卡片的左上角坐标(X坐标和Y坐标)、长和宽。两两之间用英文空格隔开。
两张长方形透明卡片的X坐标和Y坐标的取值范围为[-1000, 1000],长和宽的取值范围为[1,200]。
输出
输出一个非负整数,表示两张卡片叠加后所形成的绿色区域的面积。
样例输入
0 100 200 100
100 150 75 75
样例输出
1875

分析:模拟题意,构造结构体变量来存储矩形的最高水平线、最低水平线,最靠左垂直线以及最靠右垂直线。根据输入就可以知道两个矩形的四个点的坐标还有相对位置。自行判断一下什么情况下面积不存在,否则就推公式,得到重合部分的宽度和高度,相乘就是面积了。
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct Node{
    int u,d,l,r;
};
int fun(Node a,Node b){
    if(a.r<=b.l)return 0;
    if(a.u<=b.d||a.d>=b.u)return 0;
    int width=min(a.r,b.r)-max(a.l,b.l);
    int length=min(a.u,b.u)-max(a.d,b.d);
    return width*length;
}
int main(){
    Node a,b;
    int length1,width1,length2,width2;
    scanf("%d%d%d%d",&a.l,&a.u,&width1,&length1);
    a.d=a.u-length1,a.r=a.l+width1;
    scanf("%d%d%d%d",&b.l,&b.u,&width2,&length2);
    b.d=b.u-length2,b.r=b.l+width2;
    if(a.l<b.l)cout<<fun(a,b)<<endl;
    else cout<<fun(b,a)<<endl;
}

B:勤劳的老杨

题目描述
勤劳的老杨最近收到了一个任务清单,在这个清单上有N项不同的工作任务。对于每一项任务都给出了两个时间[X, Y],其中X表示任务的起始时间(任务从第X天开始,包含第X天),Y表示任务的结束时间(任务到第Y天结束,包含第Y天)。
认真的老杨对待每一项任务都是一心一意的。一旦他决定做某一项任务,在该任务没有完成之前他不会同时再做另一项任务,也就是说在任意时刻老杨手头最多只有一项任务。
假设完成每一项任务所获得的报酬都是相等的。那么,老杨应该如何来安排自己的时间才可以得到最多的报酬呢?
请你编写一个程序帮老杨计算出他最多可以完成的任务数量。保证至少能完成一项任务。
输入
单组输入。
第1行输入一个正整数N表示任务清单上任务的总数。(N<=1000)
第2行到第N行每一行包含两个正整数,分别表示每一项任务的开始时间和结束时间,两个正整数之间用空格隔开。
输出
输出老杨最多可以完成的任务数量。
样例输入
7
1 4
1 3
2 7
3 4
4 6
5 10
7 8
样例输出
3
提示
对于输入样例,最多可以完成的任务数量为3,对应[1, 3](第2项任务),[4, 6](第5项任务)和[7, 8](第7项任务)这三项任务。

分析:其实就是贪心的思想,直接对所有任务进行排序,越早完成的任务排越靠前(结束时间最早的),然后for循环遍历下去,满足node[i]的开始时间在node[i-1]的结束时间之后就可以啦。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct Node{
    int s,d;
}node[1005];
bool cmp(Node a,Node b){
    return a.d<b.d;
}
int main(){
    int n,num=0,t=0;
    scanf("%d",&n);
    for(int i=0;i<n;++i){
        scanf("%d %d",&node[i].s,&node[i].d);
    }
    sort(node,node+n,cmp);
    int i=0;
    while(i<n){
        if(node[i].s>t)
            num++,t=node[i].d;
        i++;
    }
    printf("%d\n",num);
}

C:秘密大厦的访客

题目描述
Kimi最近在负责一栋秘密大厦的安保工作,他的工作是记录大厦的来访者情况。
每个来访者都有一个与之对应的唯一编号,在每一条到访记录中记录了该来访者的编号。
现在Kimi需要统计每一条记录中的来访者是第几次光临秘密大厦。
输入
单组输入,每组两行。
第1行包含一个正整数n,表示记录的条数,n不超过1000;
第2行包含n个正整数,依次表示Kimi的记录中每位来访者的编号,两两之间用空格隔开。
输出
输出1行,包含n个正整数,两两之间用空格隔开,依次表示每条记录中的来访者编号是第几次出现。
样例输入
6
1 1 2 2 3 1
样例输出
1 2 1 2 1 3

分析:模拟题,可以另外开辟一个数组p,p[i]的大小就表示编号为i的来访者来的次数(开始都为1,来访一次后就+1)。下面代码直接用STL的map容器啦

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
    int n,num;
    scanf("%d",&n);
    map<int,int>m;
    while(n--){
        scanf("%d",&num);
        m[num]++;
        printf("%d ",m[num]);
    }
    printf("\n");
}

D:最大能量

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

输入
单组输入。
第1行包含两个正整数N和W,其中N<=103,W<=103。
第2行到第N+1行,每一行包含两个正整数,分别表示每一种能量包的重量和能量值,两个正整数之间用空格隔开。每一种能量包的重量和能量值都是小于等于100的正整数。
输出
输出X星人能够拥有的最大能量值。
样例输入
3 12
4 20
3 10
6 20
样例输出
50

分析:其实这题是多重背包的题目。。。但是!由于自己多重背包循环没记住,比赛时推导又出了点问题。。(太爱滚动数组,没想到关键时刻直接给忘了,错了太多次了)。
不过幸好这题的k给出来最大范围为2。。。我直接把数组扩大2倍,当做01背包的板子题来做。。幸好,运气眷顾了我。
这里直接给出几大背包问题的解析和理解,可能还是需要花不少时间来琢磨的:
背包九讲

代码如下(用的是一维的思想,节省空间):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int f[1005],c[2005],w[2005];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&c[i],&w[i]);
        c[n+i]=c[i],w[n+i]=w[i];
    }
    for(int i=1;i<=2*n;++i)
        for(int j=m;j>=c[i];--j)
            f[j]=max(f[j],f[j-c[i]]+w[i]);
    printf("%d\n",f[m]);
}

E:最大素数

题目描述
输入一个数字字符串,从中删除若干个(包含0个)数字后可以得到一个素数,请编写一个程序求解删除部分数字之后能够得到的最大素数。
例如,输入“1234”,删除1和4,可以得到的最大素数为23。
输入
单组输入。
输入一个数字字符串,字符串的长度不超过10。
输出
输出删除0个或者多个数字之后所能得到的最大素数。如果删除到最后也得不到素数则输出“No result.” 。
样例输入
1234
样例输出
23

分析:这题直接dfs暴力搜索就行,最好的剪枝,就是让string由长到短去暴搜,搜到这个素数就是找到了。判断素数用更快速的方法:六素数法

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll maxn=0;
bool judge(ll n){
    if(n==1||n==0)return false;
    if(n==2||n==3||n==5)return true;
    if(n%2==0||n%3==0)return false;
    for(ll i=5;i<=sqrt(n);i+=6)
        if(n%i==0||n%(i+2)==0)return false;
    return true;
}
ll turn(string s){
    ll num=0;
    for(int i=0;i<s.size();++i)num=num*10+s[i]-'0';
    return num;
}
void fun(string s,int x){
    if(!x){
        ll num=turn(s);
        if(num>maxn&&judge(num))maxn=num;
        return;
    }
    for(int i=0;i<s.size();++i){
        string s1=s;s1.erase(i,1);
        fun(s1,x-1);
    }
}
int main(){
    string s;
    cin>>s;
    for(int i=0;i<s.size();++i){
        fun(s,i);
        if(maxn)break;
    }
    printf("%lld\n",maxn);
}

F:最大计分

题目描述
小米和小花在玩一个删除数字的游戏。
游戏规则如下:
首先随机写下N个正整数,然后任选一个数字作为起始点,从起始点开始从左往右每次可以删除一个数字,但是必须满足下一个删除的数字要小于上一个删除的数字。每成功删除一个数字计1分。
请问对于给定的N个正整数,一局游戏过后可以得到的最大计分是多少?
输入
单组输入。
第1行输入一个正整数N表示数字的个数(N<=10^3)。
第2行输入N个正整数,两两之间用空格隔开。
输出
对于给定的N个正整数,一局游戏过后可以得到的最大计分值。
样例输入
6
3 4 3 5 2 1
样例输出
4

分析:仔细一看这个题就是求一个连续下降子序列嘛。。。可得读好题,我用的是二分的哦,这个效率快多了,就是连续上升子序列逆运用。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int N,cnt;
    scanf("%d",&N);
    cnt=0;
    int* num=new int[N];
    int* dp=new int[N];
    for(int i=0;i<N;i++)scanf("%d",&num[i]);
    dp[cnt++]=num[0];
    for(int i=1;i<N;i++){
        if(num[i]<dp[cnt-1])dp[cnt++]=num[i];
        else{
            int l=0,r=cnt-1;
            while(l<r){
                int mid=(l+r)>>1;
                if(dp[mid]<=num[i])r=mid;
                else l=mid+1;
            }
            dp[r]=num[i];
        }
    }
    printf("%d\n",cnt);
}

G:密匙

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

分析:可惜在有思路的时候时间已经不够了,其实就是搜索下去,利用递归,找到回归条件(k=1时直接返回、k=2时直接递推求,还是很快的),其它时候,要在for循环时就避免一些不必要的递归步骤。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll turn(string s){
    ll num=0;
    for(int i=0;i<s.size();++i)num=num*10+s[i]-'0';
    return num;
}
ll fun(string s,int k){
    if(k==1)return turn(s);
    if(k==2){
        ll res=1;
        for(int i=0;i<s.size();++i){
            string s1=s.substr(0,i+1),s2=s.substr(i+1,s.size()-i-1);
            ll num=turn(s1)*turn(s2);
            if(num>res)res=num;
        }
        return res;
    }
    ll res,maxn=1;
    for(int i=0;i<s.size();++i){
        string s1=s.substr(0,i+1),s2=s.substr(i+1,s.size()-i-1);
        //cout<<s1<<" "<<s2<<endl;
        for(int i=1;i<k;++i){
            if(s1.size()>=i&&s2.size()>=k-i){
                res=fun(s1,i)*fun(s2,k-i);
                //cout<<res<<endl;
                if(res>maxn)maxn=res;
            }
        }
    }
    return maxn;
}
int main(){
    string s;
    cin>>s;
    int k=s[s.size()-1]-'0';
    printf("%lld\n",fun(s,k));
}


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星币才能保证任意两栋楼之间都有光纤直接或者间接相连。
样例输入
3 10
1 2 5
1 3 6
2 3 7
样例输出
110

分析:最小生成树的板子题,最近刚复习就用到了,运气不错

有关最小生成树的讲解,可以看这篇文章:
最小生成树

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int NUM=105;
int S[NUM];
struct Edge{int u,v,w;}edge[NUM*NUM];
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
int find(int u){
	return S[u]==u?u:find(S[u]);
}
int n,m;
int kruskal(){
    int ans=0;
    for(int i=1;i<=n;++i)S[i]=i;
    sort(edge+1,edge+m+1,cmp);
    for(int i=1;i<=m;++i){
        int b=find(edge[i].u);
        int c=find(edge[i].v);
        if(b==c)continue;
        S[c]=b;
        ans+=edge[i].w;
    }
    return ans;
}
int main(){
    int c;
    scanf("%d%d",&n,&c);
    m=n*(n-1)/2;
    for(int i=1;i<=m;++i)
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    printf("%d\n",kruskal()*c);
}

总结
搜索题还是做的不够多,支支吾吾地写代码,还需要多练。下一次运气要是不行,我恐怕就不行了。除了多做搜索专题,还需要把背包问题。。。敲熟。。

举报

相关推荐

0 条评论