题意学长给的挺详细的了,就不解释了......
在学长的课件里面给出了很多的做法,这里就只说一下学长最后说的可以过的方法吧。
对于数列中的每一个‘1’,我们都可以用一个长度为T的数组来记录每一个的行动状态,即用‘1’来表示向左移动一位,用‘0’来表示静止不动(就是其前面有'1'挡路啦),例如数列0001,T=3时里面那个1的运动状态就可以表示为111(即向左移动3位),现在我们考虑先把样例里面每一个的1的运动状态都弄一下,
原数组:1 0 0 1 0 1 1 1 0 1 T=3
运动状态(表示从左到右的每一个‘1’):000 ; 110 ; 111 ; 011 ; 001 ; 101。
发现了什么没有,是不是我们先假设最初数组为000,之后我们每遇到一个‘1’,都在原数组的基础上先右移一位,再在前面插入一个‘0’,每遇到一个‘0’,都是把数组里面的第一个‘0’变为了1。其实对于这两条我们可以这样理解,对于我们扫到的一个‘1’,可能都会阻挡之后的‘1’前进的步伐,每遇到一个‘0’,就相当于后面的‘1’都可以比前面的‘1’多前进一步。相隔数个‘0’的两个‘1’,它们在前面的‘1’没有阻拦的情况下前进的步伐一样,如果有前面有阻拦且后面的还可以走,它的最终位置就是在前面的点的后面,就是砍去了前面的运动状态的最后一步(前面的'1'导致的右移一位),并且对于前面走不动的情况下,后面的点还可以进行移动,就是我们的将‘1’变为‘0’的情况。相隔数个‘1’的两个‘1’,他们前进的区别就是后面的‘1’要在前面的阻挡都清空的情况下才可以走前面的'1'的路,即我们的右移并补‘0’的操作。
这样我们剩下的问题就是维护那个队列了,我这里为方便下标修改每一个‘0’,用的是手模的deque,并且又用了一个deque来记录数列里面每个‘0’的位置,由于我们插入‘0’的操作只有向前插入‘0’,那么插入的‘0’的下标就是单调递减的,我们每次放‘0’就把‘0’的下标放入队首,队列里面的坐标就是单增的,我们每次修改从队首取出第一个‘0’的位置,用下标直接修改就可以啦~
对于答案的统计,计算的同时求一下‘1’的个数,初坐标减去该个数就是最后的位置,记录一下就行了。
(貌似好像STL的deque也可以下标修改,但juruo太菜了不会用QAQ)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=3e6+10; 4 int q[5000000],head,back; //512mb,数组随便开吧~ 5 deque<int>qq; //存‘0’的下标用 6 int a[N],ans[N],tot; //tot记录此时数列的‘1’的个数,最后该‘1’右移了tot位,用于统计答案 7 int main(){ 8 int n,k; 9 scanf("%d%d",&n,&k); 10 head=3500000; back=head-1; 11 for(int i=1;i<=k;++i){ 12 qq.push_back(back+1); //先存一下初始状态 13 q[++back]=0; 14 } 15 for(int i=1;i<=n;++i){ 16 scanf("%d",&a[i]); 17 if(a[i]){ 18 ans[i-tot]=1; //记录答案 19 if(q[back]) tot--; //进行pop队尾 20 else{ 21 if(!qq.empty()) 22 qq.pop_back(); 23 } 24 back--; 25 q[--head]=0; 26 qq.push_front(head); //push新的‘0’的下标 27 } 28 else{ 29 if(!qq.empty()){ 30 q[qq.front()]=1; 31 qq.pop_front(); 32 tot++; 33 } 34 } 35 } 36 for(int i=1;i<=n;++i) //输出答案 37 printf("%d ",ans[i]); 38 puts(""); 39 return 0; 40 }