Description
题目详情和测试样例在这里:链接
在 POJ DISCUSS 中找到的一组很有用的测试样例:
思路
建议在做这道题之前先了解 LIS (最长上升子序列) 的 DP 解法,我之前没有接触过,所以在思考子状态时出现了偏差,但也很有意思,具体是什么偏差可以看这里:链接
题目的意思是让士兵排队,要排成中间高两边低的情况,使得除任何一个士兵向左或者向右看时视野不被遮挡,但是最中间的士兵的身高可以相等。最后输出不符合要求需要出队重新调整位置的人数。
和我高中合唱团的排列一样,图例如下:
图片来源:http://cavenkaka.iteye.com/blog/1542421
我是这么想的,问题转化成求 LIS ,那么 LIS 中不符合递增要求的元素个数可以被表示为 “n - LIS” ,其中 n 是元素总个数。那么这道题就很简单了,先从左至右求 LIS ,后从右至左求 LIS ,最后找到两个 LIS 的最大和并满足两个 LIS 中的元素不能重复,并让 n 减去该最大和即可。
#include<iostream> #include<algorithm> #include<vector> #include<cstring> using namespace std; #define INT_MIN -1 const int MAX_N = 1000; int dp[MAX_N + 1]; int dp2[MAX_N + 1]; int main (void) { int n; cin >> n; vector<float> nums; nums.resize(n+1); for (int i = 1; i <= n; i++) { cin >> nums[i]; } memset(dp, 0, sizeof(dp)); dp[1] = 1; //从左到右求LIS //dp[i] 代表前i个数的最长递增子序列的长度 for (int i = 2; i <= n; i++) { dp[i] = 1; for (int j = 1; j < i; j++) { if (nums[j] < nums[i] && dp[i] <= dp[j] + 1 ) { dp[i] = dp[j] + 1; } } } //从右到左求LIS //dp[i] 代表后 n-i+1 个数的最长递增子序列的长度 memset(dp2, 0, sizeof(dp2)); dp2[8] = 1; for (int i = n; i >= 1; i--) { dp2[i] = 1; for (int j = n; j > i; j--) { if (nums[j] < nums[i] && dp2[i] <= dp2[j] + 1 ) { dp2[i] = dp2[j] + 1; } } } //求满足先递增再递减关系的串的最大长度 //注意递增序列中的元素与递减序列中的元素不能重复 int ans = INT_MIN; for (int i = 1; i < n; i++){ for (int j = i+1; j <= n; j++) { ans = std::max(ans, dp[i] + dp2[j] ); } } //cout << "满足先递增再递减的串的最大长度是:" << ans << endl; /* cout << "检查 dp2 中!" << endl; for (int i = 0; i <= n; i++) { cout << dp2[i] << " "; } cout << endl; cout << "检查完毕!!!" << endl; */ cout << n-ans << endl; return 0; }