POJ1836 Alignment
Alignment
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 18497 | Accepted: 6060 |
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.
Input
On the first line of the input is written the number of the soldiers n. On the second line is written a series of n floating numbers with at most 5 digits precision and separated by a space character. The k-th number from this line represents the height of the soldier who has the code k (1 <= k <= n).
There are some restrictions:
• 2 <= n <= 1000
• the height are floating numbers from the interval [0.5, 2.5]
Output
The only line of output will contain the number of the soldiers who have to get out of the line.
Sample Input
8
1.86 1.86 1.30621 2 1.4 1 1.97 2.2
Sample Output
4
思路:当时看到OJ分类是计算几何,就往斜率上面考虑,还考虑了线段相交。发现都不行。后来又猜想是从最高点开始,从左边找距离它最近的点(y1>y2),直到没有可以找到的点。右边同理,但是最高点的位置又不能确定。无奈之下问了队友,队友说是LIS,我也想了。感觉不行。搜了题解,真的是LIS。
LIS分为O(n2) 和 O(nlogn)的写法:
前者O(n2):定义dp[i]:=以a[i]为末尾的LIS长度 递推关系:dp[i] = max{dp[i], dp[j]+1( j<i 且 a[j]<a[i] );
代码:
double a[maxn];
int l[maxn], r[maxn];//左 右
for(int i = 0; i < n; i++) {
l[i] = 1;
for(int j = 0; j < i; j++) {
if(a[i] > a[j])
l[i] = max(l[i], l[j]+1);
}
}
//右边同理
for(int i = n; i >= 0; i--) {
r[i] = 1;
for(int j = n; j > i; j--) {
if(a[i] > a[j])
r[i] = max(r[i], r[j]+1);
}
}
//再两两枚举即可 可以看O(nlogn)写法
后者O(nlogn):
dp[i]:= 长度为i+1的上升子序列中末尾元素的最小值
思路:最开始全部初始化inf,然后逐个考虑数列元素,对于每一个a[j],如果i=0或者dp[i-1] < a[j] 的话,就用dp[i] = min(dp[i], a[j])进行更新。最终找出使得dp[i] < inf的最大值的i+1就是结果了。
例如:
int dp[maxn];
void solve() {
fill(dp, dp+n, inf);
for(int i = 0; i < n; i++) {
*lower_bound(dp, dp+n, a[i]) = a[i];
}
printf("%d\n", lower_bound(dp, dp+n, inf) - dp);
}
//模拟 3 1 2 5 4
// 插3 3 inf inf inf inf
// 插1 1 inf inf inf inf
// 插2 1 2 inf inf inf
// 插5 1 2 5 inf inf
// 插4 1 2 4 inf inf
//下标 0 1 2 3 4
//结果是 dp[i] < inf 的 最大的i+1 所有就是3
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int inf = 0x3f3f3f3f;
double a[maxn], G[maxn];//原数组 变动数组(栈)
int dp1[maxn], dp2[maxn];//小标为i 对应的最长上升子序列长度
int main() {
//freopen("in.txt", "r", stdin);
int n;
while(~scanf("%d", &n)) {
for(int i = 0; i < n; i++)
scanf("%lf", &a[i]);
fill(G, G+n, inf*1.0);
for(int i = 0; i < n; i++) {
int k = lower_bound(G, G+n, a[i]) - G;//找出第一个比inf小的值的下标
G[k] = a[i];//更新
dp1[i] = k+1;//记录i当前的LIS长度 注意+1
}
fill(G, G+n, inf*1.0);
for(int i = n-1; i >= 0; i--) {//同理
int k = lower_bound(G, G+n, a[i]) - G;
G[k] = a[i];
dp2[i] = k+1;
}
//枚举两个点 最大值就是最优解
int ans = 0;
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
ans = max(ans, dp1[i]+dp2[j]);
}
}
printf("%d\n", n-ans);//去除的人数
}
}