http://codeforces.com/contest/1154/problem/E
解题:
举例n=10,k=1
1,2,10,4,7,6,9,8,5,3
第一次,1队先挑2,10,4这三个人
1,2,10,4,7,6,9,8,5,3
第二次,2队挑6,9,8三个人
1,2,10,4,7,6,9,8,5,3
第三次,1队挑1,7,5三个人
1,2,10,4,7,6,9,8,5,3
第四次,2队挑3一个人
1,2,10,4,7,6,9,8,5,3
显然需要实现的有两点
(1)挑完后的“连接”,比如
第一次挑完后需要把1和7“连接”起来
第二次挑完后需要把7和5“连接”起来
用l数组标记当前下标 左边相邻的数的下标
用r数组标记当前下标 右边相邻的数的下标
例如一开始:
r[1]=2,表示下标为1的人(1)的右边是下标为2那个人(2)
l[5]=4,表示下标为5的人(7)的左边是下标为3那个人(4)
第一次挑完之后
r[1]=5,表示下标为1的人(1)的右边是下标为5那个人(7)
l[5]=1,表示下标为5的人(7)的左边是下标为1那个人(1)
每次挑完人后把已经挑选的人“删掉”,左右扩散找人可以通过左右数组来找人,直接跳过被挑选过的人,实现“删掉”,
(2)快速找最大值,暴力寻找肯定会超时
一般遇到n个不同的数随机出现在数组里,可以用下标数组idx标记这个数出现的下标位置,直接查找。
dix[10]=3表示10这个数在原数组的下标位置是3
这里可以从n开始减小,一直减到1,时间复杂度O(n)
#include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> #include<string> #include<vector> #include<iostream> #include<cstring> #include<set> #include<queue> #define inf 0x3f3f3f3f #define ll long long using namespace std; int n,k,maxxidx,maxx; int a[200005]; int l[200005];///下标为i的人 的左边 下标是多少 int r[200005];///下标为i的人 的右边 下标是多少 int idx[200005];///标记原数组下标 int ans[200005]; void findmax()///找最大值,主要是改变全局变量maxxidx和maxx { while(maxx>=1) { if( ans[ idx[maxx] ]==0 ) { maxxidx=idx[ maxx ]; break; } maxx--; } } int main() { while(scanf("%d%d",&n,&k)!=EOF) { memset(a,0,sizeof(a)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); memset(idx,0,sizeof(idx)); memset(ans,0,sizeof(ans)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); idx[ a[i] ]=i; l[i]=i-1; r[i]=i+1; } l[n+1]=n; r[0]=1; maxxidx=idx[n];///初始化为最大的下标 maxx=n; int now=0;///当前已经选了多少人 int t=1;///初始是1队先挑人 while(now<n) { findmax(); int i=maxxidx;///最大的那个人的下标 ans[i]=t; now++; int x=l[i]; int y=r[i];///中间向左右两边扩展 for(int j=1;j<=k;j++) { if(x>=1 && x<=n)///如果x是0表明左边无人了 ans[x]=t,x=l[x],now++;///通过左右指针数组找下一个人 if(y>=1 && y<=n) ans[y]=t,y=r[y],now++; } ///挑完人就该改左右下标了 l[y]=x; r[x]=y; if(t==1) t=2; else t=1; } for(int i=1;i<=n;i++) printf("%d",ans[i]); printf(" "); } return 0; }