8.13(優先佇列貪心維護+打表找規律+對頂堆優先佇列+DFS減枝+貪心dp)

冬天的睡袋發表於2024-08-15

nowcoder
第k小
我沒有搞清楚題目的意思。以為要全部保留所有的數,其實不然
題目只需要保留到第k位即可,我們可以開一個優先佇列去存值
查詢小於k輸出-1,大於k,pop掉

#include <bits/stdc++.h>
using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,m,k;
    cin>>n>>m>>k;
    priority_queue<int,vector<int>,less<int>>q;

    for (int i = 0; i < n; i++) {
        int a;
        cin>>a;
        q.push(a);
    }
    while(q.size()>k)q.pop();
    while(m--){
        int b;
        cin>>b;
        if(b!=1){
            if(k>q.size())cout<<-1<<'\n';
            else
                cout<<q.top()<<'\n';
        }else{
            int c;
            cin>>c;
            q.push(c);
            if(q.size()>k)q.pop();
        }
    }
    return 0;
}

Running Median
使用對頂堆可解,左邊的堆維護中位數前半部分,右邊的堆維護中位數後半部分。在新的數進來的時候判斷應該插左邊還是右邊,然後對左右如果差值超過1進行調整。
這樣要麼左堆的頂端是中位數,要麼當左右相同大小的時候就是兩個堆頂的數取平均數。
沒有怎麼練過這種題,對頂堆去維護某個值

#include <bits/stdc++.h>

using namespace std;


int p;

int main() {
    
    cin>>p;
    while (p--) {
        priority_queue<int, vector<int>, less<int> > zuo;
        priority_queue<int, vector<int>, greater<int> > you;
        int num, m, val;
        cin>>num>>m;
        vector<int> v;
        for (int i=1;i<=m;i++) {
            cin>>val;
            if (zuo.size()==you.size()) {
                if (zuo.size()==0) {
                    zuo.push(val);
                } else if (you.top()<val) 
                {
                    you.push(val);
                    zuo.push(you.top());
                    you.pop();
                } else {
                    zuo.push(val);
                }
            }else {
                if (zuo.top()>=val) {
                    zuo.push(val);
                    you.push(zuo.top());
                    zuo.pop();
                } else {
                    you.push(val);
                }
            }
            if (i&1==1) {
                if (!zuo.empty()) {
                    if (zuo.size()==you.size()) v.push_back((zuo.top()+you.top())/2);
                    else v.push_back(zuo.top());
                }
            }
        }
        cout<<num<<" "<<v.size()<<endl;
        int i;
        for (i=0;i<v.size();i++) {
            cout<<v[i]<<" ";
            if ((i+1)%10==0) {
                cout<<endl;
            }
        }
        if (i%10!=0)
        cout<<endl;
    }
    
    
    return 0;
}

tokitsukaze and Soldier
我想到怎麼去分堆去堆放,但是沒有想到怎麼去維護每個s[i]的最大時攻擊力最大,從大佬的
思路得知,我們從後往前插入優先佇列,一直往前加,如果當前的人數大於對堆內最小的k,那麼把堆內最小的攻擊力推出去
這樣就能維護每個s[i]的最大攻擊力。

思路:我們考慮按s[i]存入vectoc,從大到小列舉s[i]的值。那麼就是在所有>=s[i]計程車兵中選v最大的s[i]個。我們可以優先佇列維護。因為s[i]是減小的。所以刪除計程車兵一定在後面用不到。

#include <bits/stdc++.h>
using namespace std;
#define  LL long long

priority_queue<LL, vector<LL>, greater<LL> > q;
vector<LL> ve[100005];
int main(){

    int n; scanf("%d", &n);
    for(int i=1; i<=n; i++){
        LL v, s;
        scanf("%lld%lld", &v, &s);
        ve[s].push_back(v);
    }

    LL ans=0, sum=0;
    for(int i=n; i>=1; i--){
        for(auto u: ve[i]){
            sum+=u;
            q.push(u);
        }
        while(q.size()>i){
            sum-=q.top();
            q.pop();
        }
        ans=max(ans, sum);
    }
    printf("%lld\n", ans);

    return 0;
}

[JSOI2007]建築搶修
貪心+優先佇列,
貪心策略:
有三種

  1. 按需要時間短的來做
  2. 按最晚開始時間做(結束減去修時間)
    3.按截止的時間早晚來做

第一種雖然時間短,但是截止時間晚,做了這件事,耽誤你之前做的時間截止少的事
第二種開始時間早,但是修理時間長,那肯定也不行,修理時間完全可以做更多的事
第三種按照截止時間做,如果這件事做時間很長,但是後面已經可以做兩件或者更多事
這個時候是有問題的。導致我們本來可以做後面兩件事卻被這件事耽誤了
大佬的題解

#include <iostream>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//程式碼預處理區

const int MAX = 1e5 + 5e4 + 7;
struct Node {
    int repair, time;
    bool operator < (const Node& v) const {
        if (time == v.time) return repair < v.repair;
        return time < v.time;
    }
} info[MAX];
priority_queue<int, vector<int>, less<int> > pq;
//全域性變數區

int main() {
    IOS;
    int n; cin >> n;
    for (int i = 1; i <= n; i++) cin >> info[i].repair >> info[i].time;
    sort(info + 1, info + 1 + n);
    int time = 0;
    for (int i = 1; i <= n; i++) {
        if (time > info[i].time) continue;//已經報廢了
        if (time + info[i].repair <= info[i].time) {
            time += info[i].repair;
            pq.push(info[i].repair);
        }
        //如果可以修就直接修
        else {
            if (pq.top() > info[i].repair) {
                time -= pq.top() - info[i].repair;
                pq.pop(); pq.push(info[i].repair);
            }
        }
        //修不了就判斷他是不是比已經修了的中最划不來的划得來,划得來就換這個
    }
    cout << pq.size() << endl;
    return 0;
}

選擇困難症
這道題就是一道簡單的DFS,但是我不知道怎麼剪枝老是出問題
正確的方法是我們每sum>k時,把後面的情況全部算進去,這樣就剪枝了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105;
int k,m;
int a[10];
int v[10][N];
ll ans=0;
void dfs(int u,int val)
{
    if(u>k+1)    return;
    if(val>m)
    {
        ll temp=1;
        for(int i=u;i<=k;i++)
        {
            temp*=(a[i]+1);
        }
        ans+=temp;
        return;
    }
    for(int i=1;i<=a[u];i++)
    {
        dfs(u+1,val+v[u][i]);
    }
    dfs(u+1,val);
}

int main()
{
    while(cin>>k>>m)
    {
        ans=0;
        for(int i=1;i<=k;i++)
        {
            cin>>a[i];
            for(int j=1;j<=a[i];j++)
            {
                scanf("%d",&v[i][j]);
            }
            sort(v[i]+1,v[i]+1+a[i]);
        }
        dfs(1,0);//層數 價值
        cout<<ans<<endl;
    }
    return 0;
}

codeforces
C. Mad MAD Sum
這道題不是一點不會做,而是題目要搞清楚,要去手打幾遍,要去打表找規律
當我們跑一次發現,數列已經是不下降的了,第二次跑完已經把所有數量為1的數全部刪除了
最後剩下的都是大於等於2的數,我們把每個數加的次數累加上即可

#include <bits/stdc++.h>
using namespace std;

#define int long long


int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int sum=0;
        int a;
        cin>>a;
        vector<int>v(a+1);
        for (int i = 1; i <=a ; ++i) {

            cin >> v[i];
        }
            for(int i=1;i<=2;i++){
                int maxx=0;
                unordered_map<int,int>mp;
                for(int j=1;j<=a;j++){
                    sum+=v[j];
                    mp[v[j]]++;
                    if(mp[v[j]]>1)maxx=max(maxx,v[j]);
                    v[j]=maxx;
                }

               }

        for(int i = 1 ; i <= a ; i ++) {
            sum += v[i] * (a-i+1);
        }
       cout  << sum << "\n";
        }

    return 0;
}

C. Basil's Garden
dp思維,從後面狀態轉移到前面,如果前面的數不大於後面的和,前面的數由後面轉移加1,否則變成前面的數

#include <bits/stdc++.h>
using namespace std;

#define int long long

int f[1008611];

int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        int n;
        cin>>n;
        int maxx=0;
        vector<int>v(n+1);
        for (int i = 1; i <=n ; i++) {
            cin>>v[i];
        }
        f[n]=v[n];
        for(int j=n-1;j>=1;j--){
            if(v[j]>f[j+1]){
                f[j]=v[j];
            }else{
                f[j]=f[j+1]+1;
            }
        }
         cout<<f[1]<<'\n';
    }
    return 0;
}

相關文章