misschuer

常用链接

统计

积分与排名

百事通

最新评论

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 = 1in;
    
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;
}

posted on 2011-03-15 14:25 此最相思 阅读(479) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理