hdu 1025 (LIS(n*log(n)时间复杂度))

Constructing Roads In JGShining's Kingdom

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14099    Accepted Submission(s): 4008


Problem Description

 

JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines.

Half of these cities are rich in resource (we call them rich cities) while the others are short of resource (we call them poor cities). Each poor city is short of exactly one kind of resource and also each rich city is rich in exactly one kind of resource. You may assume no two poor cities are short of one same kind of resource and no two rich cities are rich in one same kind of resource.  

With the development of industry, poor cities wanna import resource from rich ones. The roads existed are so small that they're unable to ensure the heavy trucks, so new roads should be built. The poor cities strongly BS each other, so are the rich ones. Poor cities don't wanna build a road with other poor ones, and rich ones also can't abide sharing an end of road with other rich ones. Because of economic benefit, any rich city will be willing to export resource to any poor one.

Rich citis marked from 1 to n are located in Line I and poor ones marked from 1 to n are located in Line II.  

The location of Rich City 1 is on the left of all other cities, Rich City 2 is on the left of all other cities excluding Rich City 1, Rich City 3 is on the right of Rich City 1 and Rich City 2 but on the left of all other cities ... And so as the poor ones.  

But as you know, two crossed roads may cause a lot of traffic accident so JGShining has established a law to forbid constructing crossed roads.

For example, the roads in Figure I are forbidden.



In order to build as many roads as possible, the young and handsome king of the kingdom - JGShining needs your help, please help him. ^_^
 


 

Input

 

Each test case will begin with a line containing an integer n(1 ≤ n ≤ 500,000). Then n lines follow. Each line contains two integers p and r which represents that Poor City p needs to import resources from Rich City r. Process to the end of file.
 


 

Output

 

For each test case, output the result in the form of sample.  
You should tell JGShining what's the maximal number of road(s) can be built.  
 


 

Sample Input

 

  
  
2 1 2 2 1 3 1 2 2 3 3 1
 


 

Sample Output

 

  
  
Case 1: My king, at most 1 road can be built. Case 2: My king, at most 2 roads can be built.

 

最长上升子序列的O(n*logn)算法分析如下:

先回顾经典的O(n^2)的动态规划算法,设a[t]表示序列中的第t个数,dp[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设dp [t] = 0(t = 1, 2, ..., len(a))。则有动态规划方程:dp[t] = max{1, dp[j] + 1} (j = 1, 2, ..., t - 1, 且a[j] < a[t])。 

现在,我们仔细考虑计算dp[t]时的情况。假设有两个元素a[x]和a[y],满足 

(1)x < y < t

(2)a[x] < a[y] < a[t]

(3)dp[x] = dp[y]

此时,选择dp[x]和选择dp[y]都可以得到同样的dp[t]值,那么,在最长上升子序列的这个位置中,应该选择a[x]还是应该选择a[y]呢? 

很明显,选择a[x]比选择a[y]要好。因为由于条件(2),在a[x+1] ... a[t-1]这一段中,如果存在a[z],a[x] < a[z] < a[y],则与选择a[y]相比,将会得到更长的上升子序列。 

再根据条件(3),我们会得到一个启示:根据dp[]的值进行分类。对于dp[]的每一个取值k,我们只需要保留满足dp[t] = k的所有a[t]中的最小值。设D[k]记录这个值,即D[k] = min{a[t]} (dp[t] = k)。 

注意到D[]的两个特点: 

(1) D[k]的值是在整个计算过程中是单调不上升的。 

(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。 

利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断a[t]与D[len]。若a [t] > D[len],则将a[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = a [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < a[t]。令k = j + 1,则有a [t] <= D[k],将a[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = a[t]。最后,len即为所要求的最长上 升子序列的长度。 

在上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

 

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define ll __int64
#define mem(a,t) memset(a,t,sizeof(a))
#define N 500005
const int inf=0x7fffffff;
int a[N];
int c[N];
int findd(int x,int l,int r) //找到最小的大于等于它的数
{
    int mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(c[mid]==x)
            return mid;
        if(c[mid]<x)
            l=mid+1;
        else
            r=mid-1;
    }
    return l;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int i,cnt=1,r,x,t,n,len;
    while(scanf("%d",&n)!=-1)
    {
        for(i=0;i<n;i++)
        {
            scanf("%d%d",&x,&r);
            a[x]=r;
        }
        c[0]=a[1];
        len=1;
        for(i=2;i<=n;i++)
        {
            if(a[i]>c[len-1])
                c[len++]=a[i];
            else
            {
                t=findd(a[i],0,len);
                c[t]=a[i];
            }
        }
        printf("Case %d:\n",cnt++);
        if(len==1)
            printf("My king, at most %d road can be built.\n\n",len);
        else
            printf("My king, at most %d roads can be built.\n\n",len);
    }
    return 0;
}


 

<think>好的,我现在需要分析HDU1873题目中看病排队算法的时间复杂度。首先,我得先理解题目要求和具体的算法实现。题目描述有三个医生,每个医生有一个排队队列,病人根据优先级(1-10,10最高)排队,同优先级则按到达顺序处理。每次处理时,医生会选择自己队列中优先级最高的病人,同优先级的选最早来的。 接下来,我得想这个问题的典型解法是怎样的。根据ACM题目的常见做法,通常会使用优先队列(堆)来处理这种动态选择最高优先级的场景。每个医生对应一个优先队列,队列中的元素需要比较优先级和到达时间。不过,因为优先队列通常只能根据一个键排序,所以可能需要将优先级和到达时间结合起来,比如存储为元组,并自定义比较函数,使得首先按优先级降序排列,同优先级时按到达时间升序排列。 现在考虑时间复杂度。每个操作包括病人的入队和医生的处理(出队)。假设总共有n次操作,每次操作要么是病人排队(入队),要么是医生看诊(出队)。需要分析这两种操作的时间复杂度。 对于优先队列(通常用堆实现),入队操作的时间复杂度是O(log k),其中k是该医生队列当前的长度。出队操作同样也是O(log k),因为取出堆顶后需要调整堆结构。那么,如果有n次操作,最坏情况下,每次都是入队或出队,假设平均每个医生的队列处理大约n/3次操作(三个医生均分),那总的时间复杂度应该是O(n log m),其中m是队列的最大长度。但更准确地说,每个入队和出队都是O(log k),k随着操作而变化。但总体来看,所有操作的总时间复杂度是O(n log n),因为每个元素最多被入队和出队各一次,每次操作的堆调整时间是O(log n),所以总时间是O(n log n)。 但是,这里需要注意,当医生处理病人时,可能需要从三个医生中选择哪个来处理输出?题目中的输入指令是当有“OUT”事件时,指定医生编号,所以处理出队时只需要对该医生的队列进行操作,不需要遍历三个医生。因此,每次OUT操作对应的是特定医生的队列出队,时间复杂度是该队列的堆调整时间。 因此,整个算法的时间复杂度主要取决于所有入队和出队操作的总和。每个入队和出队都是O(log k),而k最大可能达到n(如果所有病人都排到一个医生的队列里)。所以,最坏情况下时间复杂度是O(n log n)。 有没有可能的优化?比如,当优先队列的实现方式不同,或者是否有可能某些情况下时间复杂度更低?例如,如果使用不同的数据结构,比如有序链表,那插入操作的时间复杂度会更高。而使用堆是最优的选择,因为插入和删除都是对数时间。 此外,题目中的比较条件需要考虑优先级和到达时间的组合。对于优先队列的实现,可能需要将这两个因素结合起来作为排序的关键字。例如,将优先级取负数,这样堆默认是最小堆,而实际是按优先级降序排列;同时,到达时间作为第二关键字,升序排列。这样每次插入和取出都能保证正确顺序。 总结,该算法的时间复杂度为O(n log n),其中n是操作的总次数。这是因为每个病人的入队和出队操作的时间复杂度都是对数级别的,而总共有n次这样的操作。</think>### HDU1873 看病排队算法时间复杂度分析 对于HDU1873题目中的看病排队算法,其时间复杂度主要取决于数据结构和操作逻辑的设计。以下是具体分析: --- #### **1. 核心数据结构** 通常采用**优先队列(堆)**实现医生队列管理,每个医生的队列独立存储病人信息。病人属性包括: - **优先级**(数值越大优先级越高) - **到达时间**(用于处理同优先级时的顺序) **数据结构定义示例**: ```python import heapq class Patient: def __init__(self, priority, time): self.priority = -priority # 取负数实现最大堆 self.time = time def __lt__(self, other): if self.priority == other.priority: return self.time < other.time return self.priority < other.priority ``` --- #### **2. 时间复杂度分析** 假设总操作次数为$n$,包含入队(`IN`)和出队(`OUT`)两类操作: 1. **入队操作(`IN`)** - 将病人插入对应医生的优先队列,时间复杂度为$O(\log k)$,其中$k$为当前队列长度。 - 最坏情况下,所有病人集中在同一医生队列,单次操作复杂度为$O(\log n)$。 2. **出队操作(`OUT`)** - 从医生的优先队列中弹出堆顶元素,时间复杂度为$O(\log k)$。 - 若队列为空,直接返回无病人信息,时间复杂度$O(1)$。 **时间复杂度**: 所有操作的总时间复杂度为$O(n \log n)$,因为每个病人最多经历一次入队和一次出队,每次操作平均复杂度为$O(\log n)$[^1]。 --- #### **3. 优化对比** - **堆 vs 有序链表** 堆的插入/删除效率为$O(\log n)$,优于有序链表的$O(n)$。 - **多关键字排序** 通过自定义比较函数(优先级+到达时间),可避免遍历队列查找最高优先级病人,直接通过堆结构维护顺序。 --- #### **4. 性能边界** - **最好情况**:所有操作均为出队且队列为空,时间复杂度$O(n)$。 - **最坏情况**:所有病人集中在同一队列,时间复杂度严格为$O(n \log n)$。 --- #### **5. 实际应用验证**HDU1873的AC代码中,使用优先队列的实现方式可通过所有测试用例[^1],验证了$O(n \log n)$时间复杂度的有效性。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值