首页 > 代码库 > zoj 3790 Consecutive Blocks(链表重点是思想)
zoj 3790 Consecutive Blocks(链表重点是思想)
There are N (1 ≤ N ≤ 105) colored blocks (numbered 1 toN from left to right) which are lined up in a row. And the i-th block‘s color isCi (1 ≤ Ci ≤ 109). Now you can remove at mostK (0 ≤ K ≤ N) blocks, then rearrange the blocks by their index from left to right. Please figure out the length of the largest consecutive blocks with the same color in the new blocks created by doing this.
For example, one sequence is {1 1 1 2 2 3 2 2} and K=1. We can remove the 6-th block, then we will get sequence {1 1 1 2 2 2 2}. The length of the largest consecutive blocks with the same color is 4.
Input
Input will consist of multiple test cases and each case will consist of two lines. For each test case the program has to read the integersN and K, separated by a blank, from the first line. The color of the blocks will be given in the second line of the test case, separated by a blank. Thei-th integer means Ci.
Output
Please output the corresponding length of the largest consecutive blocks, one line for one case.
Sample Input
8 1 1 1 1 2 2 3 2 2
Sample Output
4
Author: LIN, Xi
Source: ZOJ Monthly, June 2014
题意:
给你一个长度为N (1 ≤ N ≤ 105) 的序列。序列中值的范围为Ci (1 ≤ Ci ≤ 109).你可以从序列中取出至多k个。但相对位置不能动。现在问你通过取出操作能够得到数字一样的序列的最长长度。
思路:
思路是非常简单的。比赛时很快就相出了做法。但到最后都没做出来。就因为写错了一个变量。仅以此文纪念自己的逗比。改掉自己马虎的毛病。话归正传。既然我们要求的是数字一样的序列的最长长度。那么对于序列中的每一个值都有在最长序列中的可能性。所以我们只需要考虑包含了序列第i个值。且用到前面的i的值的一些的最长长度。现在关键是这个k怎么删呢。当前值设为v。如果我们知道上个出现v的位置和上个v用到了前面的那些v和上个位置的最大长度。为了使连续v最长肯定就要想办法是当前v和上个v连在一起。如果他们直接相邻的话肯定他的最长长度为上个位置最长长度加1.否则肯定要用除去中间的其他值使得序列连续。那么肯定要去掉当前位置-上个位置+1个元素。所以思路就清晰了。对于每个值维护一个链表。表示它用到了前面的那些v还有多少个k可以删。然后把这n个序列依次往自己对应的链表里加。如果k够的话就直接加。不够的话就把链表从后往前删然后恢复k。为什么删后面的呢。因为从当前位置要连续。不删前面删哪里啊。由于c比较大所以要hash一下。由于每个元素至多被加进和移除链表依次。所以最多2*n次操作。这样就华丽的以O(n)的时间复杂度解决了这道题。
详细见代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<queue> using namespace std; const int maxn=100010; int H[maxn],arr[maxn],n,m,ptr; void init()//hash初始化 { ptr=0; sort(H,H+m); m=unique(H,H+m)-H; } struct node//链表头结点 { int k,st,tail,len;//k还有k个元素可以删。st最后一个加入元素的指针。tail链表尾。方便从后往前删。len链表长度 } hd[maxn]; struct nd { int id,next;//链表结点。id为在序列中的下标。 } me[maxn]; int Hash(int x) { return lower_bound(H,H+m,x)-H; } int main() { int i,k,v,tl,p,ans; while(~scanf("%d%d",&n,&k)) { ans=1; for(i=0;i<n;i++) { scanf("%d",&arr[i]); H[i]=arr[i]; } m=n; init(); for(i=0;i<m;i++) { hd[i].k=k; hd[i].st=hd[i].tail=-1; } for(i=0;i<n;i++) { v=Hash(arr[i]); me[ptr].id=i; me[ptr].next=-1; p=hd[v].st; if(p==-1)//链表为空时直接加入 { hd[v].len=1; hd[v].st=hd[v].tail=ptr++; continue; } if(i-me[p].id-1<=hd[v].k)//惨痛教训。。。开始写的i-me[p].id-1<=k { hd[v].k-=(i-me[p].id-1);//空间还够直接加入。 me[hd[v].st].next=ptr;//注意链表指的方向。 hd[v].st=ptr++; hd[v].len++; } else { while(hd[v].k<i-me[p].id-1&&hd[v].tail!=-1)//删链表尾增加k { tl=hd[v].tail; if(me[tl].next==-1) hd[v].tail=-1; else { hd[v].k+=me[me[tl].next].id-me[tl].id-1; hd[v].tail=me[tl].next; hd[v].len--; } } if(hd[v].tail==-1) { hd[v].len=1; hd[v].st=hd[v].tail=ptr++; continue; } hd[v].k-=i-me[p].id-1;//k够了就加进来 me[hd[v].st].next=ptr; hd[v].st=ptr++; hd[v].len++; } ans=max(ans,hd[v].len); } printf("%d\n",ans); } return 0; }