题目大意:给你n个木板,每个木板有两个属性L和W,按照某种顺序进行加工。规定对于相邻加工的两个木板1,2,如果满足L1≤L2且W1≤W2,则不需要耗费时间,否则就需要耗费1单元时间。现让你根据某种加工顺序,求出最短的用时。加工第1块木板用时1单元时间。
思路:求不下降子序列的组数的问题,根据Dilworth定理,链的最小组数等于反链的最大长度,于是该问题就转化为求最长下降子序列问题,DP解决。
(1)Dilworth定理的证明:
1.对于集合P,当P只有一个元素时,结论显然成立。
2.对于集合P,a为P的最大元素,令P‘ = P / {a}。假设P' 有k组链C1,C2, ......, Ck,和一个长度为k的反链A。显然有A∩Ci ≠∅,i=1,2,3,......,k。记Ci中的最大的元素为xi,令Ai为包含xi的长度为k的反链。对于任意的i与j,满足Ai ∩ Cj ≠ ∅。令y∈Ai ∩ Cj,则显然有y ≤ xj,所以xi !≥ xj,同理可得xj !≥ xi。根据i,j的任意性,所以A为长度为k的反链。
3.讨论a,当存在i∈{1,2,3,......,k},使得a≥xi,令K={a} ∪ Ci。则P / K能被分成k-1组链,最长反链为A / {xi},长度为k-1。因此加入K后满足要求;当不存在i,使a≥xi,P能被分成k+1组链,反链为A ∪ {a},长度为k+1,同样成立。
得证。
(2)最长下降子序列问题求解:设置dp[i]表示以第i个点结尾时的最长下降序列长度,则状态转移公式为:dp[i]=max(aj),j=1,2,3......n,aj=dp[j]+1当nj<ni ;否则aj=1。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct Stick
{
int w,l;
bool operator< (const Stick &a) const
{
if (w==a.w)
return l<a.l;
return w<a.w;
}
}stick[5005];
int dp[5005];
int main(int argc, char** argv)
{
int test_cases,n,i,j,ans;
scanf("%d",&test_cases);
while (test_cases--)
{
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d%d",&stick[i].l,&stick[i].w);
sort(stick+1,stick+1+n);
dp[1]=1;
for (i=2;i<=n;i++)
for (j=1,dp[i]=1;j<i;j++)
if (stick[i].l<stick[j].l)
dp[i]=max(dp[i],dp[j]+1);
ans=-1;
for (i=1;i<=n;i++)
if (dp[i]>ans)
ans=dp[i];
printf("%d\n",ans);
}
return 0;
}