天梯選拔賽2補題_2024_03_09

ouhq發表於2024-03-10

補題1:奶茶袋收集

題意:天梯選拔賽2補題_2024_03_09天梯選拔賽2補題_2024_03_09

做法:貪心。之前還做過類似的題,賽時一直想不出來。選擇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學長的融合

題意:天梯選拔賽2補題_2024_03_09天梯選拔賽2補題_2024_03_09

做法:題目本身完全沒有難度,主要是讀題目沒讀懂升級機制是什麼,不知道怎麼樣“以此類推”。賽後讀懂題目之後輕鬆補了。

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:紅石難題

題意:天梯選拔賽2補題_2024_03_09天梯選拔賽2補題_2024_03_09

做法:思維(關鍵)+貪心--把二維座標軸拉成一條數軸(常有的做法)轉換為一維的,之後進行簡單貪心即可。

注意的點!:輸出的時候如果直接用了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:幸運號碼

題意:天梯選拔賽2補題_2024_03_09

做法:狀態壓縮,這種題也是一種型別吧。類似智乃的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:噁心的廣告

題意:天梯選拔賽2補題_2024_03_09天梯選拔賽2補題_2024_03_09

做法:賽時沒時間看題目,實際上不難。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;
}

相關文章