动态规划:洛谷P1233 木棍加工
洛谷P1233 木棍加工
洛谷的一个普及/提高-的题目,考的是二维的动态规划。
我的思路:根据本题的描述,这题在状态上有两个维度的转移,长度和宽度,我们不妨将长度从大到小先排序一下,这样只要考虑宽度就可以,问题就转化为了求排序后宽度上的不上升子序列的最小数量,根据dilworth定理,题目就转化为了求最长上升子序列的长度。我们可以用二分法来动规,时间复杂度为nlogn,二分动规的具体原理可以看我的 洛谷 P1020 [NOIP1999 普及组] 导弹拦截 的原理方法。 排序时也要注意,当长度相同时,应该return a.宽度大于b.宽度,否则答案会错。
我的代码:
1 //P1233 木棍加工 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 struct stick 7 { 8 int l,w; 9 }s[5001]; 10 bool cmp(stick a, stick b) 11 { 12 if (a.l == b.l) 13 return a.w > b.w;//当长度相同时 要按照宽度排序 14 return a.l > b.l; 15 } 16 int dp[5001]; 17 int main() 18 { 19 int n; 20 cin >> n; 21 for (int i = 0; i < n; ++i)cin >> s[i].l >> s[i].w; 22 sort(s, s + n, cmp);//先把长度排序好 再求宽度 23 //就把二维转化为了一维的dp 求这个序列最少能分割为多少个不上升的子序列 24 //根据dilworth定理 一个序列中不上升的子序列数量等于最大上升子列的长度 25 //利用二分 来动规 时间复杂度nlogn 26 int len = 0; 27 dp[len] = s[0].w;//要先给dp[0]初始化为s的第一个元素 28 for (int i = 1; i < n; ++i) 29 { 30 if (s[i].w > dp[len]) 31 dp[++len] = s[i].w; 32 else 33 { 34 int p = lower_bound(dp, dp + len, s[i].w)-dp;//用指针得到下标 因为lowerbound返回的是地址指针 35 dp[p] = s[i].w; 36 37 } 38 } 39 cout << len+1;//这个地方要加1 因为我们的dp数组是从0开始的 40 return 0; 41 42 }
通过!: