zoj 3790 Consecutive Blocks(链表重点是思想)

Consecutive Blocks


Time Limit: 2 Seconds     
Memory Limit: 65536 KB



There are N (1 ≤ N ≤ 105) colored blocks (numbered 1 to
N from left to right) which are lined up in a row. And the i-th block‘s color is
Ci (1 ≤ Ci ≤ 109). Now you can remove at most
K (0 ≤ KN) 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 integers
N 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. The
i-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;
}

zoj 3790 Consecutive Blocks(链表重点是思想)

时间: 2024-10-11 19:51:11

zoj 3790 Consecutive Blocks(链表重点是思想)的相关文章

ZOJ 3790 Consecutive Blocks

题目链接~~> 做题感悟:这题可以直接二分,也可以分段二分和枚举差不多. 解题思路: 存每个数的时候同时存每个数的下标,然后排个序先按从小到大排,再按下标从小到大排,这样相同的数都在一块,因为最多可以选 k 个,那么我们就按 K 个来,这样依次枚举每个数的最大范围(在同一个数的区间内),分段二分就可以了.感觉数据有点水,写完竟然 1 A . 代码: #include<iostream> #include<sstream> #include<map> #includ

2014 Super Training #8 B Consecutive Blocks --排序+贪心

当时不知道怎么下手,后来一看原来就是排个序然后乱搞就行了. 解法不想写了,可见:http://blog.csdn.net/u013368721/article/details/28071241 其实就是滑动窗口的思想. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algor

《UML和模式应用》重点之思想篇

本书是帮助开发者和学生学习面向对象分析和设计(OOA/D)的核心技能的重要工具. UML不是OOA/D,也不是方法,只是图形表示法,如果没有真正掌握如何创建优秀的面向对象设计,或者如何评估和改进现有设计,那么学习UML或者UML CASE工具是毫无意义的.对象思想才是重点和难点. 在OO开发中,至关重要的能力是熟练地为软件对象分配职责,除此之外当然还有其他很多重要的技能. 有益的分析和设计可以概括为:做正确的事(分析)和正确地做事(设计). 面向对象分析的过程中强调在问题领域内发现和描述对象(或

linux内存源码分析 - 内存回收(lru链表)

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 对于整个内存回收来说,lru链表是关键中的关键,实际上整个内存回收,做的事情就是处理lru链表的收缩,所以这篇文章就先说说系统的lru链表. 内存回收的核心思想,就是如果一些数据能够保存到磁盘,在内存不足时就把这些数据写到磁盘中,这样这些数据占用的内存页就可以作为空闲内存页给予系统使用了. 当内存不足时,系统就必须要将一些页框回收,而哪些页框可以回收呢,之前我们有说过,属于内核的大部分页框是不能够进行回

ZOJ Monthly, June 2014 解题报告

A.Another Recurrence Sequence B.Gears 题目大意:有n个齿轮,一开始各自为一组,之后进行m次操作,包括以下4种类型: 1.合并两组齿轮,合并的两个应该反向旋转 2.把某个齿轮从所在组删除,自为一组,但不影响同组其它齿轮的状态与关系 3.询问两个齿轮是同向.反向或无关系(即不在同一组) 4.询问某个齿轮所在组的齿轮总数 分析:典型的并查集操作,但是注意两点: 1.由于操作3要询问两个齿轮的相对状态,因此对并查集中每个元素应当保存它的状态信息.状态是相对的,只需要

线性表 及Java实现 顺序表、链表、栈、队列

数据结构与算法是程序设计的两大基础,大型的IT企业面试时也会出数据结构和算法的题目, 它可以说明你是否有良好的逻辑思维,如果你具备良好的逻辑思维,即使技术存在某些缺陷,面试公司也会认为你很有培养价值,至少在一段时间之后,技术可以很快得到提高.同时,它也是软考的重点,我们需要对这部分的内容进行一下总结. 我们先看一下数据结构和算法的整体内容. 1.线性表 概念: 数据元素的排列方式是线性的. 分类: 分类规则是根据上图中元素的存储结构来划分的. (1)顺序表 基本思想:元素的存储空间是连续的.在内

数据结构——反转单链表

最近看了<剑指offer>这本书,遇到了一个问题:反转链表 题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点. 链表结点定义如下: struct ListNode { int _data; ListNode * _pNext; }; 解决方案如下: ListNode * ReverseList(ListNode * pHead) { ListNode * pRevesedHead = nullptr;//反转后的头结点 ListNode * pNode = pHe

【转】链表归并排序插入排序

链表插入排序.链表归并排序 1.链表 1.1链表的存储表示 1 2 3 4 5 6 7 //链表的存储表示 typedef int ElemType; typedef struct LNode {     ElemType data;     struct LNode *next; }LNode, *LinkList; 1.2基本操作 创建链表: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

C++ 动态链表

C++ 动态链表 用类写的 头文件代码: 1 #include<iostream> 2 #include<string> 3 //动态创建链表 4 using namespace std; 5 class LNode { 6 private: 7 string StudentNum; 8 string Name; 9 int age; 10 LNode *next; 11 public: 12 LNode() {}//构造函数 13 ~LNode() {}//析构函数 14 voi