牛客算法竞赛入门笔记2

优先队列、并查集

Running Median (nowcoder.com)

题意:给你n个数,算出前i个数的中位数。

这题挺善良的,只让输出奇数个的时候的中位数,不然可太麻烦了,我们维护两个优先队列,一个代表小于中位数的值,一个代表大于中位数的值,这样中位数永远都是数量较大的那个队列的队首元素,但是添加的时候要注意,一次添加两个,不然偶数个的时候就不知道中位数是谁了,这也就是为什么我说这题还做了个人。添加完之后要记得维护一下这两个队列的大小关系,让他们的元素数量差不超过1,这样才能保证一定取到中位数。

这个做法叫对顶堆

 #include <bits/stdc++.h>
 using namespace std;
 int x[10001];
 priority_queue<int>le;
 priority_queue<int,vector<int>,greater<int>>ri;
 int main(){
     int t;
     cin>>t;
     while(t--){
         int n,num;
         cin>>num>>n;
         for(int i=0;i<n;i++){
             cin>>x[i];
         }
         //清空一下,第一发mle,不知道是不是因为我两个队列设的是局部变量
         while(le.size()!=0){
             le.pop();
         } 
         while(ri.size()!=0){
             ri.pop();
         } 
         le.push(x[0]);
         int k=0;
         cout<<num<<" "<<(n+1)/2<<endl;
         int now=x[0];
          //这里加等于是为了输出最后一轮的中位数,不然会少一个
         for(int i=1;i<=n;i+=2){
             if(le.size()>ri.size()){
                 now=le.top();
             }else{
                 now=ri.top();
             }
             cout<<now<<" ";
             if(i==n||i==n+1){
                 break;
             }
             //第二发wa在这:10个一行,但是最后一行如果满十个不用输出k
             k++;
             if(k%10==0){
                 cout<<endl;
             }           
             if(x[i]>now){
                 ri.push(x[i]);
             }else{
                 le.push(x[i]);
             }
             if(i==n-1){
                 break;
             }
             if(x[i+1]>now){
                 ri.push(x[i+1]);
             }else{
                 le.push(x[i+1]);
             }
             while(fabs(le.size()-ri.size())>1){
                 if(le.size()>ri.size()){
                     int t=le.top();
                     le.pop();
                     ri.push(t);
                 }else{
                     int t=ri.top();
                     ri.pop();
                     le.push(t);
                 }
             }
         }
         cout<<endl;
     }
     return 0;
 }

tokitsukaze and Soldier (nowcoder.com)

 

一道贪心+优先队列,我们将士兵按照s从大到小排列,因为这样我可以选择的人数才是递增的,不像我从小到大排列会造成一上来就有n个可以选择的数的情况。然后每当我要加入一个人数比当前队伍人数少的士兵时,我将价值最小的几个士兵剔除,答案一定出现在其中某一步操作的时候。

 #include <bits/stdc++.h>
 using namespace std;
 #define ll long long
 struct node{
     ll v,s;
     bool operator<(const node&a)const{
         return v>a.v;
     }
 };
 node x[100001];
 ​
 bool com(node a,node b){
     if(a.s==b.s){
         return a.v>b.v;
     }
     return a.s>b.s;
 }
 ​
 int main(){
     int n;
     cin>>n;
     for(int i=0;i<n;i++){
         cin>>x[i].v>>x[i].s;
     }
     sort(x,x+n,com);
 //  for(int i=0;i<n;i++){
 //      cout<<x[i].v<<" "<<x[i].s<<endl;
 //  }
     priority_queue<node>q;
     ll ans=0,now=0;
     for(int i=0;i<n;i++){
         q.push(x[i]);
         now+=x[i].v;
         while(q.size()>x[i].s){
             now-=q.top().v;
             q.pop();
         }
         ans=max(ans,now);
     }
     cout<<ans<<endl;
     return 0;
 } 

感觉自己越学越菜了是怎么回事,这两场cf1300的题都a不出来,真是tfw.

[JSOI2007]建筑抢修 (nowcoder.com)

和上一道一样的题,贪心+优先队列,感觉就是堆贪心啊,之前我练过,只不过有点忘了,做了上面那题想起来一点,就不放代码了,水题。

Problem - D - Codeforces

2300的题,雨巨真的是离谱,居然教我2300的题,但是这题看下来就是一个贪心+优先队列,但是数据很烦,我被卡常数了,艹。

题意:n台电脑,每台初始电量为ai,每秒消耗bi,问最少要一个功率多少的电源能让他们坚持k秒,但是一分钟内,电源只能给一个电脑充电。

思路:一看就是二分,然后每次判断的时候我们贪心地给当前看起来最坚持不下去的充电,看看能否坚持k秒。

 #include <bits/stdc++.h>
 using namespace std;
 #define ll long long
 struct node{
     ll a,b,s;//这个s代表比值,我就是这里被卡了,前几发全都是判断a/b,但是有了这个就不用那么多次计算除法了,绝了。
     bool operator<(const node m)const{
         return s>m.s;
     }
 };
 ll n,k;
 node x[200001];
 priority_queue<node>q0;
 inline bool fun(ll m){
     priority_queue<node>q(q0);
     ll ans=0;
     while(q.top().s<k-1){
         if(q.top().s<ans){
             return false;
         }
         node t=q.top();
         q.pop();
         t.a+=m;
         t.s=t.a/t.b;
         q.push(t);
         ans++;
         if(ans>k){
             return false;
         }
     }
     return true;
 }
 ​
 int main(){
     std::ios::sync_with_stdio(false);
     scanf("%d%d",&n,&k);
     for(int i=0;i<n;i++){
         scanf("%lld",&x[i].a);
     }
     for(int i=0;i<n;i++){
         scanf("%lld",&x[i].b);
     }   
     for(int i=0;i<n;i++){
         x[i].s=x[i].a/x[i].b;
         q0.push(x[i]);
     }
     ll l=0,r=1e13;
     ll ans=1e13+1;
     while(l<=r){
         ll mid=(l+r)/2; 
         if(fun(mid)){
             ans=min(ans,mid);
             r=mid-1;
         }else{
             l=mid+1;
         }
     }
     if(ans==1e13+1){
         printf("-1\n");
         return 0;
     } 
     printf("%lld\n",ans);
     return 0;
 }

第 k 小 (nowcoder.com)

给一个会动态增长的数组,求第k小的数

这题看上去好像不难,但是因为数据范围太大,要求插入和查询都不能超过O(logn)因此想到了用优先队列,这个队列里只存放前k个小的数,因为k个以后的数是不可能成为答案的,所以不用纪录他们。

 #include <bits/stdc++.h>
 using namespace std;
 int x[200001];
 int main(){
     int n,m,k;
     cin>>n>>m>>k;
     for(int i=0;i<n;i++){
         cin>>x[i];
     }
     priority_queue<int>q;
     for(int i=0;i<n;i++){
         if(q.size()<k){
             q.push(x[i]);
         }else{
             if(q.top()>x[i]){
                 q.pop();
                 q.push(x[i]);
             }
         }
     }
     while(m--){
         int ca;
         cin>>ca;
         if(ca==1){
             int b;
             cin>>b;
             if(q.size()<k){
                 q.push(b);
             }else{
                 if(q.top()>b){
                     q.push(b);
                     q.pop();
                 }
             }           
         }else{
             if(q.s
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wuhudaduizhang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值