# LIS # POJ 1836 Alignment

POJ 1836

Description
In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight line in front of the captain. The captain is not satisfied with the way his soldiers are aligned; it is true that the soldiers are aligned in order by their code number: 1 , 2 , 3 , … , n , but they are not aligned by their height. The captain asks some soldiers to get out of the line, as the soldiers that remain in the line, without changing their places, but getting closer, to form a new line, where each soldier can see by looking lengthwise the line at least one of the line’s extremity (left or right). A soldier see an extremity if there isn’t any soldiers with a higher or equal height than his height between him and that extremity.

Write a program that, knowing the height of each soldier, determines the minimum number of soldiers which have to get out of line.

题目大意:

令原队列士兵出列,使得新队列呈三角形分布(即为中间高,两边依次逐渐降低),问最少出列士兵数。

解题思路:

分别根据 a[i] 求从头到 a[i] 的最长上升子序列和从尾到 a[i] 的最长下降子序列,将两个子序列长度相加得出的结果寻找出其中最大值,然后输出 n - 最大值即可。(本题代码在最后)

解题关键:

DP求LIS

LIS ( O(n * n) ):

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <algorithm>  
#include <queue>  
#include <map>  
#include <set>  
using namespace std;  

int a[1005];  
int dp[1005]; //用来存以a[i]为结尾的最长上升子序列的长度  

int main() {  
    int n;  
    scanf("%d", &n);  
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);  
    int ans = 0;  
    for(int i = 1; i <= n; i++) {  
        dp[i] = 1;  
        for(int j = 1; j < i; j++)  {  
            if(a[j] < a[i] && dp[j] + 1 > dp[i])  
                dp[i] = dp[j] + 1;  
        }  
        ans = max(ans, dp[i]); //最长上升子序列的长度  
    }  
    return 0;  
}  

LIS ( O(nlogn) ):

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <algorithm>  
#include <queue>  
#include <map>  
#include <set>  
using namespace std;  
#define INF 0x3f3f3f    

int a[1005];  
int dp[1005]; //用来存以a[i]为结尾的最长上升子序列的长度  

int main() {    
    int n;    
    scanf("%d", &n);    
    for(int i = 1; i <= n; i++) {    
        scanf("%d", &a[i]);    
        dp[i] = INF;//不可以用memset对数组赋值INF,可以用fill(dp, dp + n, INF)  
    }    
    for(int i = 1; i <= n; i++)  
        *lower_bound(dp, dp + n, a[i]) = a[i];  //找到大于等于a[i]的第一个元素,并用a[i]替换  
    printf("%d\n", lower_bound(dp, dp + n, INF) - dp);  //找到第一个INF的地址减去首地址就是最大子序列的长度  
    return 0;    
}  

PS:memset() 与 fill() 的区别:

memset() :

  • 因为memset()是按照字节去赋值的,所以int数组只能被赋值为0(在二进制层面全为0)或者是-1(在二进制层面全为1)
  • memset()函数还能将int型数组初始化为INF(0x3f3f3f3f)
  • 对于字符数组:char a[100]; memset( a, ‘/0’, sizeof(a) ); memset( a, ’ ‘, sizeof(a) );
  • 对于由结构体定义变量:struct sample_strcut stTest; memset( &stTest, 0, sizeof(struct sample_struct) );
  • 对于由结构体定义的数组:struct sample_struct TEST[10]; memset( TEST, 0, sizeof(struct sample_struct)*10 );

fill():

fill(arr, arr + n, val):按照数组的始末位置以一个数组元素为单位赋值,将区间内的每一个元素都赋值为val。

本题代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
using namespace std;

double h[1005];
int dp1[1005], dp2[1005];

int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%lf", &h[i]);
    for(int i = 1; i <= n; i++) {
        dp1[i] = 1;
        for(int j = 1; j < i; j++)  {
            if(h[j] < h[i] && dp1[j] + 1 > dp1[i])
                dp1[i] = dp1[j] + 1;
        }
    }
    for(int i = n; i >= 1; i--) {
        dp2[i] = 1;
        for(int j = i + 1; j <= n; j++) {
            if(h[i] > h[j] && dp2[j] + 1 > dp2[i]) {
                dp2[i] = dp2[j] + 1;
            }
        }
    }
    int maxx = 0;
    for(int i = 1; i <= n; i++) 
        for(int j = i + 1; j <= n; j++)
            maxx = max(maxx, dp1[i] + dp2[j]);
    printf("%d\n", n - maxx);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jasmineaha/article/details/79190894
lis