题目大意:两个教练(1号和2号)选队员,选择规则是当前能力值最大的队员(确保所有队员能力不一样),在选择最大队员同时,其左右各k个队员也同时被选中。1号教练先选。
解题思路:暴力算法很容易想到,先循环找到最大值,再左右两侧各找k个点。复杂度是。
为降低复杂度,先按能力值排序,这样可以直接定位当前最大值。那么左右两侧k个点怎么处理?如果用循环找的话还是可能复杂度。这样可以考虑用双链表来维护,比如原序列是(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;
}