重新振作第四天----NBUACMer-Beginnerround10.24题解

题目链接:https://ac.nowcoder.com/acm/contest/94181

A:StringGame (思维)

题意介绍:给出一个长度为n的字符串s以及操作次数x,我们有一个操作,可以将字符串s的第一个字符放在s的后面,并将第一个删掉。问经过x次操作后,问字符串s最后是什么样子的。

数据范围: 1<=n<=1E5 1<=x<=1E18

思路:可以看成一个环,如果当操作了n次时,就又回到了原样。所以我们要求的是在模n意义下的操作次数x,即x=x%n。然后我们就先输出后面的n-x个字符,再输出前x个字符即可。

复杂度:O(n)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long n,x;
    string s;
    cin>>n>>x;
    cin>>s;
    x=x%n;
    if(x==0)
        cout<<s<<endl;
    else 
    cout<<s.substr(x,n-x)<<s.substr(0,x)<<endl;
}

B:SequenceGame (贪心+二分)

题意介绍:简单来说,就是给你n组数,每组数有m个字,问当n组数中只能取一个数字或者不取时,所形成的最长上升子序列的长度是多少。

数据范围:n<1E4 a[i]<=10

思路:类似于最长上升子序列的贪心+二分做法,唯一的不同点就是多了一个分组。对于最长上升子序列,我们的做法是,建立一个数组f,下标表示长度,元素值代表在这个长度下末尾的最小元素值。既然如此,我们在此基础上,建立一个临时的数组f1,具体的含义也是如上所示。但是,我们二分的时候,还是对于原来的f进行操作,对于f1进行更新,这样就避免更新的操作影响到组内元素的操作,最后再用f1对于f进行相应的更新即可。例如,一组数 1,2,3。无论如何,最长上升子序列的长度也只能为1,但是,如果不建立临时的数组f1,而是直接对于f进行修改的话,那么在操作后f1 = 1,f1 = 2,f1 = 3。而如果建立了临时的f1数组,就可以避免修改对于当前组内的后续操作造成影响,这样操作的结果就是f1 = 1。其余的步骤,均和最长上升子序列一致。

复杂度:O(nmlogn)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,m;
    cin>>m>>n;
    vector<vector<int>>a(n+1,vector<int>(m+1,0));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
        }
    }
    int len=0;
    vector<int>f(n+1,90000000);
    vector<int>f1(n+1,90000000);
    for(int i=1;i<=n;i++){
        int templen=len;
        for(int j=1;j<=len;j++){
            f1[j]=f[j];
        }
        for(int j=1;j<=m;j++){
            int l=1,r=templen+1,mid;
            while(l<r){
                mid=(l+r)>>1;
                if(f[mid]>=a[i][j])r=mid;
                else l=mid+1;
            }
            f1[l]=min(f1[l],a[i][j]);
            len=max(len,l);
        }
        for(int j=1;j<=len;j++){
            f[j]=f1[j];
        }
    }
    cout<<len<<endl;
    
}

C:照看小猫 (几何,思维)

题意介绍:给出n个点,这些点全部位于第一象限以及x,y的正半轴。我们可以在这些点当中任意连线,在满足所连线段和x,y正半轴能够组成封闭图形的前提下,线段的长度和最小。

数据范围: \(1 \le n \le 10^5 , 0 \le x_i,y_i \le 10^9\),同时不存在(0,0)

思路:首先,要是能围住,那么一定在x,y正半轴上面至少各有一个点,否则无法练成。那么最小的长度,肯定是距离x轴最近的y轴上的点,以及距离y轴最近的x轴上的点,这两点所形成的线段最短。因为三角形,两边之和大于第三边。如果要结合其他点,那必然会形成三角形,就会大于这条线段。
(PS:恶心之处,不关闭同步流,会出现超时现象)

复杂度:O(n)

代码实现:

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int main(){
    ios;
    int n;
    double x=1E18,y=1E18,u,v;
    int f1=0,f2=0;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>u>>v;
        if(u==0){
            f1=1;
            y=min(y,v);
        }
        if(v==0){
            f2=1;
            x=min(u,x);
        }
    }
    if(f1&&f2){
        cout<<fixed<<setprecision(6)<<sqrtl(x*x+y*y)<<'\n';
    }
    else {
        cout<<"Poor Little H!"<<'\n';
    }
}

D:小H的数列 (数学)

题意介绍:一个数列满足a1 = 1,4ai-1ai = (ai-1 +ai -1)2。给出数字n,求出第n项的值。

数据范围:n<1E(10086)

思路: 把上面的公式化简一下,就可以得到\(\sqrt[]a_i\)-\(\sqrt[]{a_{i-1}}=1\),我们不难看出,这个其实是完全平方数,因为只有完全平方数开平方之后的值相减才是1。我们直接输出n的平方即可,但是考虑到数据范围,我们不如直接用python,省得去找C++高精度的模板。

复杂度:O(1)

代码实现:

n=int(input())
print(n*n)

E:小H的糖果 (思维)

题意介绍:总共有T组测试,每组给出两个整数k,n。我们一开始的数字是1,我们可以进行两种操作,第一种是直接+1,第二种是*k。求从1变化到n的最小操作次数是多少。

数据范围:n<1E4 a[i]<=10

思路:这题是思维题,当k为1的时候,我们采取第二种操作其实没有任何意义,因此我们直接输出n-1即可。而当k不等于1的时候,我们反向计算,如果n大于等于k的时候,我们直接先求要减去多少次,才可以使得当前的n是k的倍数,即ans=ans+n%k+1,然后我们再更新n,让n整除k。由此进行,n最终的值只能小于k。这个时候答案其实很明显,就是ans+k-1。那么为什么这个方法是可以的呢?首先,我们知道k的变化速度可能要大于+1,那么肯定能使用k越多越好,但是这样又会存在万一多乘了该怎么办,我们不存在-1的操作。这个时候我们不如反向思考,既然我们从小可以得到大,我们也会再某个时候+1,那不如直接就反过来,如果能整除k,肯定优先整除,否则减去再整除,这样我们既可以在保证不会多计算的同时次数最少。相当于一个数的k进制一般。这个才是思考的关键部分。在前面部分的+1,其实就是在更高的进制位上面+1,因为我们后面会存在*k操作。
(PS:这题还有个恶心点就是,不能使用endl,只能用“\n”,否则会超时)。

复杂度:O(Tlogn)

代码实现:

#include<bits/stdc++.h>
using namespace std;
long long t;
long long k,n,ans;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>t;
   for(int i=1;i<=t;i++){
        cin>>k>>n;
        if(k==1){
            cout<<n-1<<'\n';
            continue;
        }
        ans=0;
        while(n>=k){
            ans+=n%k;
            ans++;
            n/=k;
        }
        cout<<ans+n-1<<'\n';
    }
}

F:学姐的编码1.0 (DP)

题意介绍:给出一个长度为n的字符串,字符串由十六进制编码组成。求不重复的递增子序列有多少个?

数据范围: \(1 \le n \le 10^6\)

思路:首先,我们考虑当第i个字符是B时,我们考虑前i-1个字符,如果存在十六进制下小于B的字符,那么我们以B结尾的子序列,就是前面以十六进制下小于B的字符结尾的子序列之和+1。这样的情况下,如果后面又再次遇到B的时候,会出现重复的情况,但我们可以注意到的是,前一个B所形成的子序列,肯定是后一个B所形成子序列的子集,也就是说后面的包含了前面的。如果那么我们不妨每次再次遇到相同字母的时候,进行一个初始化,然后再进行操作即可。

复杂度:O(16n)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s;
    cin>>s;
    vector<int>a(17,0);
    int n=s.size();
    s=' '+s;
    for(int i=1;i<=n;i++){
        if(s[i]>='A'&&s[i]<='F'){
            a[s[i]-'A'+11]=1;
            for(int j=0;j<s[i]-'A'+11;j++){
                a[s[i]-'A'+11]+=a[j];
            }            
        }
        else {
            a[s[i]-'0']=1;
            for(int j=0;j<s[i]-'0';j++){
                a[s[i]-'0']+=a[j];
            }
        }
    }
    long long ans=0;
    for(int i=0;i<17;i++){
         //cout<<a[i]<<endl;
        ans+=a[i];
    }
    cout<<ans<<endl;
}

G:学姐的编码2.0 (DFS)

题意介绍:给出一个长度为n的字符串,字符串由十六进制编码组成。按字典序输出所有不重复的递增子序列。

数据范围:\(1 \le n \le 10^6\)

思路:直接dfs暴力求解,但是直接找的话会超时,我们可以用二分法,判断是否存在符合位置的下标。

复杂度:O(\(2^{16}logn\))

代码实现:

#include<bits/stdc++.h>
using namespace std;
int ans[17];
vector<int>ff[17];
void dfs(int len,int x,int p){
    for(int i=0;i<len;i++){
        if(ans[i]<10)
            cout<<ans[i];
        else {
            cout<<char(ans[i]-10+'A');
        }
        if(i==len-1){
            cout<<endl;
        }
    }
    for(int i=x+1;i<16;i++){
        if(ff[i].size()){
            int p1=upper_bound(ff[i].begin(), ff[i].end(), p)-ff[i].begin();
            if(p1!=ff[i].size()){
                ans[len]=i;
                dfs(len+1,i,ff[i][p1]);
            }
        }
    }
}
int main(){
    string s;
    cin>>s;
    int n=s.size();
    for(int i=0;i<n;i++){
        if(s[i]>='0'&&s[i]<='9'){
            ff[s[i]-'0'].push_back(i);
        }
        else {
            ff[s[i]-'A'+10].push_back(i);
        }
    }
    dfs(0,-1,-1);
}

H:迷阵 (BFS+双指针)

题意介绍:有一个\(n \times n\) 的矩阵,每一个位置上的元素都有一个权值,每个位置都可以被经过人一次,要求从(1,1)到(n,n)所经过路径的最大权值和最小权值之差是多少?

数据范围:\(1 \le n \le 100 , w_{i,j} \le 3000\)

思路:一开始,我是想枚举最小值,然后二分最大值,接着bfs判断能否到达,但是这样会超时(3000* n* n* log(max\(a_i\)))(dfs不会。。。。。????)。所以参考别人的写法,发现可以使用一种类似双指针的方法去处理这道题。初始化l和r为0,先判断能否到达,如果不能到达,则r++,直到能够到达,当能够到达时,则l++,直到不能到达。之所以这样是可以的,是因为我们本质上是从小的l开始,找到第一个符合的r,然后再固定r,找到不符合的l,缩小了区间。然后再次迭代。因为最后肯定会存在一个l和r吧,这样确保了l和r都会经历到,相当于每次都找到了(l,r)符合的最小子段。

复杂度:O(6000nn)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int vis[101][101];
int vis1[101][101];
int n,maxx=0;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};
bool bfs(int l,int r){
    memset(vis1,0,sizeof vis1);
    queue<pair<int,int>>q;
    q.push({1,1});
    vis1[1][1]=1;
    if(min(vis[1][1],vis[n][n])<l||max(vis[1][1],vis[n][n])>r){
        return false;
    }
    int f=0;
    while(q.size()){
        if(q.front().first==n&&q.front().second==n){
            return true;
        }
        for(int i=0;i<4;i++){
            int x=q.front().first+dx[i];
            int y=q.front().second+dy[i];
            if(x>=1&&x<=n&&y>=1&&y<=n&&vis[x][y]<=r&&vis[x][y]>=l&&!vis1[x][y]){
                vis1[x][y]=1;
                q.push({x,y});
            }
        }
        q.pop();
    }
    return false;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>vis[i][j];
            maxx=max(maxx,vis[i][j]);
        }
    }
    int l=0,r=0;
    int res=maxx;
    while(l<=maxx&&r<=maxx){
        if(bfs(l,r)&&r>=l){
            //cout<<r<<' '<<l<<endl;
            res=min(r-l,res);
            l++;
        }
        else r++;
    }
    cout<<res<<endl;
}

I:Rating (思维+优先队列)

题意介绍:首先,给出n,m,分别表n个cf号以及m场比赛表现分,每一次比赛,会选择一个cf号的分数,与表现分相加并除以2。求最终的可能最高分时多少。

数据范围:$n,m \le 10^5 $

思路:每一次都选择最低分数,这样万一得分比最低分高,则总分增加最多,比最低低,则总分减少最少。这个,每次选择最低分的过程,我们可以使用优先队列。

复杂度:O(nlogn)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    priority_queue<double,vector<double>,greater<double>>q;
    int n,m;
    cin>>n>>m;
    double ans=0;
    for(int i=1;i<=n;i++){
        double x;
        cin>>x;
        q.push(x);
        ans+=x;
    }
    for(int i=1;i<=m;i++){
        double x,y;
        x=q.top();
        q.pop();
        cin>>y;
        q.push((x+y)/2);
        ans=ans-x+(x+y)/2;
        cout<<fixed<<setprecision(2)<<ans<<endl;
    }
}

J:字符串修改 (暴力)

题意介绍:给出一个字符串s,当下标为奇数时,\(s_i\)变为\(s_i + i\),否则,变为\(s_i\)变为\(s_i - i\)。字符加法被定义为a+1=b......z+1=a,减法同理。

数据范围:\(1 \le n \le 10^5\)

思路:其实也是在模意义下,找出原先的数,即\(s_i - a\),然后在\(+-i\)之后,取模即可。

复杂度:O(n)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    string s;
    cin>>n;
    cin>>s;
    s=' '+s;
    for(int i=1;i<=n;i++){
        if(i&1){
            cout<<(char)((s[i]-'a'+i)%26+'a');
        }
        else {
            cout<<(char)(((s[i]-'a'-i)%26+26)%26+'a');
        }
    }
}

K:New Game! (计算几何+最短路)

题意介绍:给出两条平行直线和n个圆。在直线上和圆形中移动不消耗体力,否则消耗的体力为欧氏距离。求从直线l1到直线l2的最小花费体力。

数据范围:\(1 \le n \le 1000\) 坐标值的绝对值小于等于10000

思路:直接暴力计算出,将直线和圆形看成一个个地点,然后暴力计算出两两之间的距离,然后暴力进行Dijkstra一遍即可。

复杂度:O(n^2)

代码实现:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,a,b,c1,c2;
    cin>>n>>a>>b>>c1>>c2;
    vector<double>dis(n+2,1E9+7),x(n+2),y(n+2),r(n+2);
    vector<vector<double>>edge(n+2,vector<double>(n+2,0.0));
    edge[0][n+1]=abs(c1-c2)*1.0/sqrt(a*a+b*b);
    edge[n+1][0]=abs(c1-c2)*1.0/sqrt(a*a+b*b);
    for(int i=1;i<=n;i++){
        cin>>x[i]>>y[i]>>r[i];
        edge[0][i]=max(0.0,1.0*abs(a*x[i]+b*y[i]+c1)/sqrt(a*a+b*b)-r[i]);
        edge[i][0]=max(0.0,1.0*abs(a*x[i]+b*y[i]+c1)/sqrt(a*a+b*b)-r[i]);
        edge[i][n+1]=max(0.0,1.0*abs(a*x[i]+b*y[i]+c2)/sqrt(a*a+b*b)-r[i]);
        edge[n+1][i]=max(0.0,1.0*abs(a*x[i]+b*y[i]+c2)/sqrt(a*a+b*b)-r[i]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            edge[i][j]=max(0.0,1.0*sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))-r[i]-r[j]);
        }
    }
    dis[0]=0;
    vector<bool>vis(n+2,0);
    for(int i=0;i<=n+1;i++){
        double maxd=1E9+10;
        int p=-1;
        for(int j=0;j<=n+1;j++){
            if(!vis[j]&&maxd>dis[j]){
                maxd=dis[j];
                p=j;
            }
        }
        //cout<<p<<' '<<dis[p]<<endl;
        vis[p]=true;
        for(int j=0;j<=n+1;j++){
            dis[j]=min(dis[p]+edge[p][j],dis[j]);
        }
    }
    cout<<dis[n+1]<<endl;
    
}
posted @   菜dog的日常生活  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示