1. 程式人生 > >POJ #1836 型如" E[j] = opt{D+w(i,j)} "的簡單DP 線性DP

POJ #1836 型如" E[j] = opt{D+w(i,j)} "的簡單DP 線性DP

ref 排隊 style net gpo string 思考 ide size

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; }
View Code

  

POJ #1836 型如" E[j] = opt{D+w(i,j)} "的簡單DP 線性DP