hdu 1025 Constructing Roads In JGShining's Kingdom

hdu 1025 Constructing Roads In JGShining's Kingdom

/*
设d[i]为以i结尾的最长上升子序列的长度,那么它的前一个元素是什么呢?
设它为j,则d[i] = max{d[j]} + 1,其中枚举条件是j < i且a[j] < a[i]。
状态有O(n)个,决策O(n)个,时间复杂度为O(n^2)。

下面考虑如何把它优化到O(nlogn)。
把同一个i对应的d[i]和a[i]看成一个二元组(d, a)。
在计算d[i]时,考虑所有满足j < i的二元组(d, a)。
显然它们的i是无关紧要的,只需要找出其中a[j] < a[i]的最大d[j]即可。

换句话说,程序看起来应该是这样的:
其中getmax(S,a[i])表示在集合S中所有a值小于a[i]的二元组中寻找d的最大值。
update(S,a[i],d[i])表示用二元组a[i]; d[i]更新集合S。

现在问题的关键是实现集合S。考虑二元组(5, 4)和(5, 3)。它们d值相同但a值不同。
对于后续决策来说,(5, 4)是合法时(5, 3)一定合法,而且(5, 4)和(5, 3)一样好。

因此我们规定:只保留(5, 3)!换句话说:d值相同的a只保留一个。
用g[i]表示d值为i的最小a,那么一定有g[1] < g[2] < …… < g[n]
(想一想,为什么?)。不存在的d值对应的g设为正无穷。
d[i]的计算可以使用二分查找得到大于等于a[i]的第一个数j,
则d[i] = j(本来是找不大于a[i]的最后一个数j,则d[i] = j + 1,
但这样转化后更方便)。g的更新由于g[j] > a[i],且i的d值为j,
因此需要更新g[j] = a[i]。
程序如下:

fill (g , g + n , infinity );

for(int i = 0; i < n; i ++){

  int j = lower_bound (g , g + n , a[ i ]) - g;
  d[ i ] = j + 1;
  g[ j ] = a[ i ];
}
6
1 6 2 4 3 7
*/

#include 
< iostream >
#define  MAXN 500001
using   namespace  std;


int  n, dp[MAXN], len, g[MAXN];

int  binarySearch( int   * M,  int  a) {

    
int  left  =   1 , right  =  len, cnt  =   0 ;

    
while (left  <=  right) {
    
        
int  mid  =  (left  +  right)  >>   1 ;
        
// = 适用于不降递增子序列
         if (a  >=  M[ mid ]) {
        
            
if (cnt  <  mid) cnt  =  mid;
            left 
=  mid  +   1 ;
        }
        
else  right  =  mid  -   1 ;
    }
    
return  cnt;
}

void  res() {
  
    
int  cc, tmp;
    len 
=   1 ; g[  1  ]  =  dp[  1  ];
    
    
// g[i] 表示 d值为i 的最小dp

    
for ( int  i  =   2 ; i  <=  n;  ++  i) {

        tmp 
=  binarySearch(g, dp[ i ]);
        cc 
=  tmp  +   1 ;
        
if (cc  >  len) {
        
            g[ cc ] 
=  dp[ i ];
            len 
++ ;
        }
        
else   if (g[ cc ]  >  dp[ i ]) {
        
            g[ cc ] 
=  dp[ i ];
        }
    }

    
if (len  >   1 )
        printf(
" My king, at most %d roads can be built.\n\n " , len);
    
else
        printf(
" My king, at most %d road can be built.\n\n " , len);
}


int  main() {

    
int  z  =   1 in ;
    
while ( ~ scanf( " %d " & n)) {
    
        
for ( int  i  =   1 ; i  <=  n;  ++  i) {
        
            scanf(
" %d " & in );
            scanf(
" %d " & dp[  in  ]);
        }

        printf(
" Case %d:\n " , z  ++ );

        res();
    }
    
return   0 ;
}

你可能感兴趣的:(hdu 1025 Constructing Roads In JGShining's Kingdom)