CF 552 E. Two Teams (排序+双链表)

传送门:Problem - E - Codeforces

题目大意:两个教练(1号和2号)选队员,选择规则是当前能力值最大的队员(确保所有队员能力不一样),在选择最大队员同时,其左右各k个队员也同时被选中。1号教练先选。

解题思路:暴力算法很容易想到,先循环找到最大值,再左右两侧各找k个点。复杂度是O(n^{2})

为降低复杂度,先按能力值排序,这样可以直接定位当前最大值。那么左右两侧k个点怎么处理?如果用循环找的话还是可能复杂度O(n^{2})。这样可以考虑用双链表来维护,比如原序列是(1,2,3,4,5),1的next是2,如2,3,4被选走了,那么1的next是5。如果有这种数据结构,每次找k个点时就一定只循环k次。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,k,v[200005],pre[200005],nex[200005];
struct node
{
    int v,id;
    bool operator <(const node y)const
    {
        return v>y.v;
    }
}a[200005];
int main()
{
    int i,j,k,l,m,x=1,pos;
    cin>>n>>k;
    for(i=1; i<=n; i++)
    {
        cin>>a[i].v;
        a[i].id=i;
        pre[i]=i-1,nex[i]=i+1;/**< 静态双链表 */
    }
    sort(a+1,a+n+1);/**< 按能力从大到小排序 */
    nex[n]=0;
    for(i=1;i<=n;i++)
    {
        if(v[a[i].id]) /**< 这个点已经被选走了 */
            continue;
        for(j=1,l=a[i].id;j<=k+1&&l;j++,l=pre[l])/**< 向左走k个点,如果l为0说明没有结点,结束循环 */
            v[l]=x;
        for(j=1,m=a[i].id;j<=k+1&&m;j++,m=nex[m])
            v[m]=x;
        pre[m]=l,nex[l]=m;/**< 修改链表的值,注意无需对所有点进行操作,例如双链 1 2 3 4 5,
        只要让1 和 5连接起来,相当于中间的2 3 4 就被删掉了 */
        x==1?x=2:x=1;/**< 切换下教练,也可以这么写  x=x%2+1 */
    }
    for(l=1; l<=n; l++)
        cout<<v[l];
    return 0;
}

也可以用set来存储结点,同样能降低复杂度,如果最大值下标是x,那么在set中找到k个大于x和k个小于x的点,将其标记后从set中删除。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,k,v[200005];
struct node
{
    int v,id;
    bool operator <(const node y)const
    {
        return v<y.v;
    }
};
priority_queue <node> pq;
set <int> st;
int main()
{
    int i,j,k,l,m,x=1,pos;
    cin>>n>>k;
    for(i=1; i<=n; i++)
    {
        node a;
        cin>>a.v;
        a.id=i;
        pq.push(a);
        st.insert(i);
    }
    while(!pq.empty())
    {
        node a=pq.top();
        pq.pop();
        if(v[a.id])
            continue;
        pos=a.id;
        vector<int> va;
        set<int>::iterator it=st.find(pos);
        for(j=0; j<=k; j++)
        {
            v[*it]=x;
            va.push_back(*it);
            if(it==st.begin())
                break;
            it--;
        }
        it=st.find(pos);
        it++;
        for(j=1; j<=k; j++)
        {
           if(it==st.end())
                break;
            v[*it]=x;
            va.push_back(*it);
            it++;
        }
        for(i=0;i<va.size();i++)
            st.erase(va[i]);
        x==1?x=2:x=1;
    }
    for(l=1; l<=n; l++)
        cout<<v[l];
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值