天梯选拔赛2补题_2024_03_09

补题1:奶茶袋收集

题意:

做法:贪心。之前还做过类似的题,赛时一直想不出来。选择k个连续的的区间,就是需要添加k-1个挡板。问题是挡板设置在哪里?可以发现一个连续线段的max-min等于线段中各个差值之和。如果k=1,那么ans=∑(ai+1-ai);如果k=2,那么需要添加一个挡板。贪心地放,挡板应该放在最大的ai+1-ai中。然后答案为剩下的ai+1-ai。

void solve(){               //补L1-7--奶茶袋收集-贪心。之前写过一模一样的题。。现在还想歪了,往前缀和想。。
    int n,k,x,ans=0,last=-1;
    cin>>n>>k;
    vector<int> dif;
    for(int i=1;i<=n;i++){
        cin>>x;
        if(i>=2) dif.emplace_back(x-last);
        last=x;
    }
    sort(dif.begin(),dif.end(),greater<int>());
    for(auto d:dif){
        if(k>1) k--;
        else ans+=d;
    }
    cout<<ans;
}

补题2:swj学长的融合

题意:

做法:题目本身完全没有难度,主要是读题目没读懂升级机制是什么,不知道怎么样“以此类推”。赛后读懂题目之后轻松补了。

vector<pair<int,int>> vct[100005];
int x,m,a,b,c,d,ans=0;
void dfs(int step){
    for(auto to:vct[step]){
        ans+=to.second;
        dfs(to.first);
    }
}
void solve(){               //补L2-2--swj学长的精灵融合--建图dfs..赛时题目都没读懂,不知道等级升级系统(类似金铲铲)是怎么样都,不知道怎么以此类推..
    cin>>x>>m;
    for(int i=1;i<=m;i++){
        cin>>a>>b>>c>>d;
        int exp=1;
        if(d==1) exp=0;
        if(c==1) exp+=( (d-2) * (1+d-2) )/2;
        if(c==2) exp+=( (d-2) * (2+2*(d-2) ) )/2;
        if(c==3) exp+=( (d-2) * (5+5*(d-2)) )/2;
        //cout<<exp<<endl;
        vct[a].emplace_back(b,exp);
    }
    dfs(x);
    cout<<ans;
}

补题3:红石难题

题意:

做法:思维(关键)+贪心--把二维坐标轴拉成一条数轴(常有的做法)转换为一维的,之后进行简单贪心即可。

注意的点!:输出的时候如果直接用了ceil,要控制输出小数点,ceil返回的不是整型,输出会带小数点。还有就是特判m=0的情况;

还有最最重要一点!length初始值是1,因为计算的是长度,实际上要的是数轴上端点的个数。而起始点上没有算上的,要自己加上!!!!比如长度为5的数轴,是有6个点的。长度是端点间的间隙,有5个间隙的话,那么需要6个点。

void solve(){           //补L1-5红石难题--思维(关键)+贪心--把二维坐标轴拉成一条数轴(常有的做法)转换为一维的,之后进行简单贪心即可-好题!
    //有一个很关键的点!!--红石信号的传导只和他放置的红石顺序有关!--这是可以拉成一条数轴的关键,传递是一维传的,是在一条线上传的.
    //一个红石信号源可以让周期d=(15-m)*2+1个红石达到能量m及以上。
    //贪心的放。结果需要的红石为;ceil(数轴的长度/d)
    int n,m,x,y,length=1,ans;
    cin>>n>>m;
    int d=(15-m)*2+1;
    pair<int,int> last;
    for(int i=1;i<=n;i++){
        cin>>x>>y;
        if(i>=2) length+=abs(last.first-x)+abs(last.second-y);
        last={x,y};
    }
    //ans=length/d+length%d?1:0;                 //运算顺序问题 wrong!!
    //ans=length/d+(length%d?1:0);

    ans=ceil(1.0*length/d);
    if(m==0) cout<<"0";         //特判

    //else cout<<ans;
    //else cout<<(int)ceil(1.0*length/d);    //ok

    //else cout<<ceil(1.0*length/d);                //返回值类型不是int,输出会带小数点 wrong!!  加一个fixed<<setprecision(0)才行
    else cout<<fixed<<setprecision(0)<<ceil(1.0*length/d);
}

补题4:幸运号码

题意:

做法:状态压缩,这种题也是一种类型吧。类似智乃的36倍数。但是这题还简单一点,不用考虑数字的性质,只要求各位和即可。提前预处理每个数字的位数奇偶和各位之和,存在cnt[2][100]中,便于后面直接从cnt中进行查询符合的个数。

再对每一个数字进行切割处理,用vct储存每一个合法的切法,切完之后所需的那个数值。之后再遍历一次vct里面的数,在cnt中查询符合的个数。全部加起来即是ans.

int ans=0;
int cnt[2][100];   //1奇 0偶--二维为各位之和。预处理好cnt数组,方便查询
vector<pair<int,int>> vct;  //存放每个数字剪了之后,需要拼在左边或右边的数值及奇偶性
//这题因为拆位有问题,改了很久。还是不够熟悉,写的不够多,一位一位拆右边的位写的多。一位一位拆左边就明显不够熟练了,出现各种问题而且还没一下意识到错误。导致改了两个小时..
//拆右边的数字是取余'%',拆左边的数字是除法'/'
void solve(){                   //补L2-3幸运号码--!!状态压缩!!--字符串!拼接!--对于每一个数字,它可能作为左部出现,也可能作为右部出现.切割的点也可能有多个.
    int n,x;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>x;
        int digit=0,sum=0,x0=x;
        while(x0){
            digit++;
            sum+=x0%10;
            x0/=10;
        }
        cnt[digit&1][sum]++;
        if(digit==1) vct.emplace_back(x,1);
        else {
            x0=x;
            int cnt0=0,leftsum=sum,rightsum=0;
            while(leftsum>rightsum){
                vct.emplace_back(leftsum-rightsum,digit&1);
                leftsum-=x0%10;
                rightsum+=x0%10;
                x0/=10;
            }
            //int num=10*(digit-cnt0-1);  wa!!!!
            int num=pow(10,digit-cnt0-1);
            x0=x,cnt0=0,leftsum=0,rightsum=sum;   //这里只是一位,不用各位相加!!! 细节!!
            bool check=false;
            while(rightsum>0&&rightsum>leftsum){            //作为右部和作为左部的代码是不一样的!!细节非常不一样!!!
                if(check) vct.emplace_back(rightsum-leftsum,digit&1);   //第一个不放入,因为上面已经放入过了..
                check=true;
                num=pow(10,digit-cnt0-1);
                if(num==0) break;
                int cursum=0,xx0=x0/num;
                while(xx0){
                    cursum+=xx0%10;
                    xx0/=10;
                }
                leftsum=cursum;             //作为右部的细节和左部的不一样!!!改了两个小时....
                rightsum=sum-cursum;
                cnt0++;
                //leftsum+=x0/num;  //这里还是有问题!!例如22222,22222/1000=22,只是得到了数字,而不是2+2=4;
                //rightsum-=x0/num;
            }
        }
    }
    for(auto v:vct) ans+=cnt[v.second][v.first];
    cout<<ans;
}

补题5:恶心的广告

题意:

做法:赛时没时间看题目,实际上不难。bfs+优先队列即可。

注意的点:不管当前打不打得过,都要放到优先队列里面。优先队列要重载运算符。power小的放在队顶。遇到第一个打不过的就可以break了。

而且如果遇到的是强化剂,应该马上加到ability。怎么重载运算符也是一个点☝️,而且这个是跟平时写的cmp是反着的。return a.power > b.power; 才是小的在堆顶.

//struct pair_hash              //unordered_map存放pair需要传入哈希函数
//{
//    template<class T1, class T2>
//    std::size_t operator() (const std::pair<T1, T2>& p) const
//    {
//        auto h1 = std::hash<T1>{}(p.first);
//        auto h2 = std::hash<T2>{}(p.second);
//        return h1 ^ h2;
//    }
//};
//unordered_map<pair<int,int>,int,pair_hash> mp;
int n,m,x0,cc,t,ability=-1;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
vector< vector<int> > vct;
map<pair<int,int>,int> mp;
map<pair<int,int>,int> vis;
typedef struct myp{
    int xx,yy;
    int power;
    friend bool operator < (const myp &a,const myp &b){       //重载运算符
        return a.power>b.power;     //power从小到大排序,和cmp是反着的!!!
    }
}myp;
priority_queue<myp> pq;
void solve(){               //补L2-4恶心的广告--赛时连题目都没看,没时间看,也没什么人过这题--赛后一眼bfs+优先队列,写写看...
    cin>>n>>m>>x0>>cc>>t;
    for(int i=0;i<n;i++){
        vector<int> cur;
        for(int j=0;j<m;j++){
            int z;
            cin>>z;
            cur.emplace_back(z);
        }
        vct.emplace_back(cur);
    }
    for(int i=1;i<=t;i++){
        int a,b;
        cin>>a>>b;
        mp[{a-1,b-1}]=1;
    }
    myp cur;
    cur.xx=x0-1,cur.yy=cc-1,cur.power=vct[x0-1][cc-1];
    pq.emplace(cur);
    vis[{x0-1,cc-1}]=1;         //是x0-1,cc-1,而不是x0,cc
    while(pq.size()){
        cur=pq.top();
        pq.pop();
        if(ability==-1) ability=cur.power;
        else if(ability>=cur.power&&!mp[{cur.xx,cur.yy}]) ability+=cur.power;
        else if(!mp[{cur.xx,cur.yy}]) break;
        //有可能出现一种情况就是:到了一个点,上下左右。发现都打不过,再去看别的点,别的点有打得过点,提升了能力。但是已经回不去当时打不过那些点了。
        //怎么样在可达都所有点中,把所有都强化剂先拿了,再去打人
        //不管打不得过,全部加入到优先队列中,取出来到时候再判!!!--
        for(int i=0;i<4;i++){
            int x=cur.xx+dx[i];
            int y=cur.yy+dy[i];
            if(x>=0&&x<n&&y>=0&&y<m&&!vis[{x,y}]){
                myp zz;
                zz.xx=x,zz.yy=y,zz.power=vct[x][y];
                pq.emplace(zz);
                vis[{x,y}]=1;
                if(mp[{x,y}]) ability+=vct[x][y];
            }
        }
    }
    cout<<ability;
}

 

posted @ 2024-03-10 18:13  osir  阅读(2)  评论(0编辑  收藏  举报