//380K 79MS G++
#include <cstdio>
#include <cstring>
using namespace std;
double soldiers[1005];
struct soldierInfo{
double prevHeight;
int num;
};
typedef struct soldierInfo soldierInfo;
soldierInfo aux[1005];
int tmpSolider[1002];
int soldierNum;
int lengthMAX;
#define INF 999999
// void solve() {
// int beginSoldierId = 1;
// lengthMAX = -INF;
// memset(aux, 0, sizeof(aux));
// while(1) {
// if (beginSoldierId > soldierNum){
// break;
// }
// int thisTimeSoldierNum = 0;
// if (aux[beginSoldierId].num == 0) {
// printf("C %d", beginSoldierId);
// int i = 0;
// aux[beginSoldierId].num = 1;
// aux[beginSoldierId].prev
// tmpSolider[thisTimeSoldierNum++] = beginSoldierId;
// for (i = beginSoldierId; i <= soldierNum; i++) {
// if (soldiers[i] < soldiers[beginSoldierId]) {
// if (aux[i].num > 0) {
// for (int i = 0; i < thisTimeSoldierNum; i++) {
// int soldierId = tmpSolider[i];
// aux[soldierId].num = thisTimeSoldierNum - i + aux[i].num;
// }
// break;
// } else {
// tmpSolider[thisTimeSoldierNum++] = i;
// // thisTimeSoldierNum++;
// }
// }
// }
// if (i == soldierNum + 1) {
// printf("B %d\n", beginSoldierId);
// for (i = 0; i < thisTimeSoldierNum; i++) {
// printf("V %d %d\n", tmpSolider[i], thisTimeSoldierNum - i);
// int soldierId = tmpSolider[i];
// aux[soldierId].num = thisTimeSoldierNum - i;
// }
// }
// }
// printf("%d %d\n", aux[beginSoldierId].num, beginSoldierId);
// lengthMAX = lengthMAX > aux[beginSoldierId].num ? lengthMAX : aux[beginSoldierId].num;
// beginSoldierId++;
// }
// printf("%d\n", soldierNum - lengthMAX);
// }
int DP[1005];
int DP2[1005];
int solve2() {
memset(DP, 0, sizeof(DP));
memset(DP2, 0, sizeof(DP2));
// jiang
for (int i = soldierNum; i >= 1; i--) {
if (i == soldierNum) {
DP[i] = 1;
} else {
int MAXLEN = 1;
for (int j = i+1; j <= soldierNum; j++) {
// printf("%d %d %lf %lf\n", i, j, soldiers[i], soldiers[j]);
if (soldiers[j] < soldiers[i]) {
int length = DP[j] + 1;
MAXLEN = MAXLEN > length ? MAXLEN: length;
}
}
DP[i] = MAXLEN;
}
// printf(" < %d %d\n", DP[i], i);
}
//sheng
for (int i = 1; i <= soldierNum; i++) {
if (i == 1) {
DP2[i] = 1;
} else {
int MAXLEN2 = 1;
for (int j = 1; j <= i-1; j++) {
// printf("%d %d %lf %lf\n", i, j, soldiers[i], soldiers[j]);
if (soldiers[j] < soldiers[i]) {
int length = DP2[j] + 1;
MAXLEN2 = MAXLEN2 > length ? MAXLEN2: length;
}
}
DP2[i] = MAXLEN2;
}
// printf(" > %d %d\n", DP2[i], i);
}
int MAXLEN3 = DP2[soldierNum] > DP[1] ? DP2[soldierNum] : DP[1];
// printf("%d\n", MAXLEN3);
if (soldierNum >= 3) {
for (int split = 2; split <= soldierNum-1; split++) {
// memset(DP, 0, sizeof(DP));
// memset(DP2, 0, sizeof(DP2));
// //jiang
// int maxlength1 = 0;
// int jiangBegin = split;
// for (int i = soldierNum; i >= split + 1; i--) {
// if (soldiers[i] < soldiers[split]) {
// if (i == soldierNum) {
// DP[i] = 1;
// } else {
// int MAXLEN = 1;
// for (int j = i+1; j <= soldierNum; j++) {
// // printf("%d %d %lf %lf\n", i, j, soldiers[i], soldiers[j]);
// if ((soldiers[j] < soldiers[i]) &&
// (soldiers[j] < soldiers[split]) &&
// (soldiers[i] < soldiers[split])) {
// int length = DP[j] + 1;
// MAXLEN = MAXLEN > length ? MAXLEN: length;
// }
// }
// DP[i] = MAXLEN;
// }
// }
// if (maxlength1 < DP[i]) {
// maxlength1 = DP[i];
// jiangBegin = i;
// }
// // maxlength1 = DP[i] > maxlength1 ? DP[i]: maxlength1;
// // printf(" > %d %d\n", DP[i], i);
// }
// //sheng
// int maxlength2 = 0;
// int shengEnd = split;
// for (int i = 1; i <= split-1; i++) {
// if (soldiers[i] < soldiers[split]) {
// if (i == 1) {
// DP2[i] = 1;
// } else {
// int MAXLEN2 = 1;
// for (int j = 1; j <= i-1; j++) {
// // printf("%d %d %lf %lf\n", i, j, soldiers[i], soldiers[j]);
// if ((soldiers[j] < soldiers[i]) &&
// (soldiers[j] < soldiers[split]) &&
// (soldiers[i] < soldiers[split])) {
// int length = DP2[j] + 1;
// MAXLEN2 = MAXLEN2 > length ? MAXLEN2: length;
// }
// }
// DP2[i] = MAXLEN2;
// }
// }
// if (maxlength2 < DP2[i]) {
// maxlength2 = DP2[i];
// shengEnd = i;
// }
// // maxlength2 = DP2[i] > maxlength2 ? DP2[i]: maxlength2;
// // printf(" < %d %d\n", DP2[i], i);
// }
int maxlength1 = 0;
int maxlength2 = 0;
int jiangBegin = split;
int shengEnd = split;
//sheng
for (int i = 1; i <= split-1; i++) {
if (soldiers[split] > soldiers[i]) {
if (maxlength1 < DP2[i]) {
shengEnd = i;
maxlength1 = DP2[i];
}
}
}
//jiang
for (int i =soldierNum; i >= split+1; i--) {
if (soldiers[split] > soldiers[i]) {
if (maxlength2 < DP[i]) {
jiangBegin = i;
maxlength2 = DP[i];
}
}
}
int heightEqualNum = 0;
int heightEqualNum1 = 0;
for (int i = shengEnd + 1; i <= jiangBegin -1; i++) {
if (soldiers[i] == soldiers[split]) {
heightEqualNum++;
if (heightEqualNum >=2) {
heightEqualNum1 = 1;
break;
}
}
}
// printf("%d %d\n", shengEnd, jiangBegin);
// printf("%d %d %d %d\n", split, maxlength1, maxlength2, heightEqualNum);
int tmpMAXLEN = maxlength1 + maxlength2 + 1 + heightEqualNum1;
MAXLEN3 = tmpMAXLEN > MAXLEN3 ? tmpMAXLEN: MAXLEN3;
// printf("%d %d\n", split, MAXLEN3);
}
}
printf("%d\n", soldierNum - MAXLEN3);
}
int main() {
while(scanf("%d", &soldierNum) !=EOF) {
for (int i = 1; i <= soldierNum; i++) {
scanf("%lf", &soldiers[i]);
}
// solve();
solve2();
}
}
发现自己现在对DP又有点手生了,连基本的最长递增序列的求法都给忘了。
这道题难不在DP本身,而在于一些细节处理,题目的要求比较苛刻:
调整队列以后,每个士兵都能至少看到最左边或者最右边的人,注意,是或者,并且又是左右两边,这样就不是一个单纯的求最长递增/递减序列了,
因为可以在中间找一个最高的士兵,然后向两边递减,这就成了一个最长递增序列和最短递增序列的组合了
比如 对于队列 S:
3 4 5 1 2 5 4 3这组队列可以这样排:
3 4 5 4 3, 最高的士兵5在中间,然后向左与右递减,但是,注意了,这还不是最优的解!因为题目只要求了能看到最左/最右,那么可以这么排:
3 4 5 5 4 3,
这样排,两个身高为5的士兵在中间,分别可以看到最左/右,是一个最优解(最长的队列)。
这样,考虑到这些因素,本题就变得有些麻烦了,
先要对整个数组求一次 最长递增/递减序列,
相应的DP数组:
DP1[]对应递增,DP1[i] 表示 以S[i] 结尾的(从S[1]开始的)递增序列的最长长度。
DP2[]对应递减,DP2[i]表示 以S[i]开头的(以S[N]结束的)递减序列的最长长度,
可以得到这两种方式下的最长序列 LX LY,
然后还有枚举每个士兵,考虑以此士兵最为最后队列的中间最高者进行求队列长度,
如果以士兵 i 作为中间最高者,那么就要求其左边的最长递增序列和右边的最长递减序列,要注意的是,这里的递增序列最大值不能搞过S[i], 同样递减序列最大值也不能超过S[i], 我在这里犯迷糊了,还又重新求了一次递增和递减,其实根本不用的,因此之前的DP1和DP2已经包含了这个信息:
对于S[i]左边, 只需求出最大的DP1[j], 且S[j] <S[i] 即可,同样,右边,只需求出最大的DP2[j], S[j] < S[i]即可,
还有一个细节: 因为要考虑 i左边/右边可以有一个和自己一样身高的士兵(只能一边有,不能两边都有),
这个一样身高的士兵,如果有的话,且符合题目需求的话,必然在最后的队列是和i紧挨的(不挨的话,就不可能递增/递减了), 因此,
设i 左边的最长递增序列的结束位置是 left, i右边的最长递减序列的开始位置是 right,
那么 如果有符合条件的同样身高士兵m, 那么分布只能是这样的:
....left.....m.....i.....right 或 .....left....i....m.....right,
通过这个就可以检测是否有士兵m了,遍历从 left+1 到 right-1的序列,如果有 >1(包含 i 本身,因此要>1表示除了i还有别的一样身高的士兵)个 和i同样身高的士兵,那么就有m存在,但只能选择一个m(如果有两个m,就不满足题了),
到了这一步,基本完成了,但还有个细节, 如果 左边/右边的最长递增/递减 序列有多个同等长度的最长序列,那么left 和 right应该 选择 left最小 ,right最大的序列,
这样能保证挑选m的序列最长,如果有m存在,就一定能找到. 还有就是考虑i在最左和最右的情况了, 这时候,左边/右边有一边不用考虑,
左边最长递增序列长度L1和和右边最长递减序列长度L2的初始值要是0(比如 5 6 9, 选6做i的话,左边,右边都没有比i小的递增/递减序列)
最后的长度是 L1 + L2 + 1 (如果有m存在) + 1(i 本身就是最终序列的一部分)
结合之前的LX LY ,以及遍历每个点的最后长度,找到的最长序列就是最终序列,
要出列的人数,就是原来队列长度减去最终序列长度即可.
这道题很考察细节,以后应该多练练这种题.