-
输入
-
第一行是一个正正数N(0<N<10),表示测试数据组数,
每组测试数据的第一行是一个正正数n,表示该组测试数据中含有矩形的个数(n<=1000)
随后的n行,每行有两个数a,b(0<a,b<100),表示矩形的长和宽
输出
- 每组测试数据都输出一个数,表示最多符合条件的矩形数目,每组输出占一行 样例输入
-
1 10 1 2 2 4 5 8 6 10 7 9 3 1 5 8 12 10 9 7 2 2
样例输出
-
-
5
若x能镶嵌于y时,x与y就有一条有向边,并且是不可能存在环的;
dp最重要的是状态和状态转移方程,可以从以下考虑:
1)状态:如果dp(i)表示最多能镶嵌几层,那么我们就要求dp(0);
2)状态如何转移或者说有哪些决策,我们在这个状态下能做什么:显而易见,我们只能顺着有向边走,即找个比当前矩形大的走,dp(i)-->dp(j) |(i,j)∈E;
3)对当前状态来说,那么多决策选哪个好:当然是最大的好,求的就是最大值,每一步都求最大,那么总的肯定最大,有点像贪心;
综上 dp(i)=max{dp(j)+1|(i,j)∈E}; 注:dp(i)的值用个数组保存;
我觉得对于dp还有一种思路理解:
若将所有有向边的画出来,就是一棵树;
我们要求得就是根的最大树高;
同时对于每一层节点来说他们的高度取决于他们最高的子树的高度+1;
所以将dp(i)想象成第i个节点的最大高度就好;
那么状态转移方程也可以那么想:dp(i)=max{dp(j)+1|(i,j)∈E}; i节点的树高就是他儿子中最高的那个+1;
dp有两种方法
1)递推方法(一般两个for循环,外面那个for是1~状态数,里面那个for是1~决策总数)
从终点出发回到起点,也即是从树的单子叶节点出发,逆着层序遍历回到根,那么对于每一节点的高就十分简单就推出来,好比数塔问题就是这样;
2)记忆化搜索(一般递归函数)
从起点(根)出发,即一个状态出发,取最有利的路走到下一状态;
dp简单来说就是每种路径都走一遍,得出最有利的路径,便走这条路;
至于路径打印就逆着推就好了;根据d[i],因为走一步就+1,所以比d[i]小1的肯定就是i的下一步;
下面是该题目的代码:
#include<iostream> #include<cstring> #include<algorithm> using namespace std; int d[500],G[50][50],n; struct Rectangle{ int x; int y; Rectangle(int x=0,int y=0):x(x),y(y) {} }; ostream& operator << (ostream& out,const Rectangle A) { out << "(" << A.x <<','<<A.y<<')'; } int dp(int i) { int& ans = d[i]; if(ans > 0) return ans; //表示该点值已经计算过了,直接返回 ans = 1; for(int j=0;j<n;j++) if(G[i][j]) ans=max(ans,dp(j)+1); return ans; } void print_ans(int i) { cout<<i<<' '; for(int j=0;j<n;j++) if(G[i][j]&&d[i]==d[j]+1) { print_ans(j); break; } } void print(int *A,int m) //打印最小字典序的答案 { for(int i=0;i<m;i++) cout<<A[i]<<' '; putchar('\n'); } void print_allans(int *A,int i,int cur) //打印所有答案 { A[cur]=i; if(d[i]==1){ print(A,cur+1); } else for(int j=0;j<n;j++) { if(G[i][j]&&d[i]==d[j]+1) { print_allans(A,j,cur+1); } } } bool operator < (Rectangle A,Rectangle B) { if((A.x<B.x&&A.y<B.y)||(A.y<B.x&&A.x<B.y)) return true; return false; } int main() { int k; Rectangle R[500]; cin>>k; while(k--) { memset(d,0,sizeof(d)); memset(G,0,sizeof(G)); cin>>n; int m=n; int i=0; while(m--) { cin>>R[i].x>>R[i].y; i++; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(i!=j&&R[i]<R[j]) G[i][j]=1; sort(R,R+n); cout<<dp(0); // int a[100]; // print_allans(a,0,0); // print_ans(0); if(k) cout<<endl; } }
接下来系统总结一下:
-
状态有两种实现方法:1)设d(i)为从i点出发的最长路,则d(i)=max{dp(j)+1|(i,j)∈E}; 即 从前往后,起点出发;
-
2)设d(i)为从i点结束的最长路,则d(i)=max{dp(j)+1|(j,i)∈E}; 即 从后往前,终点出发;
-
实现方法: 1)记忆化搜索:上面的状态1)法,正着递推,也即是填表法:对于每一个状态i,找到f(i)依赖的所有状态; 如:f(i) 的值要靠f(Vj)算 出来,其中Vj为i的相连点,于是去算f(Vj);
-
2)递推:上面的状态2)法,反着递推,也称为刷表法:是从Vj到i ,是因为每个d[Vj]会影响到d[i]的数值,所以“对于每个状态i,更 新f(i)所影响到的状态”,类似Dijkstra 更新i点周围所有相邻点Vj的d[Vj]值;
dp题目思路理解 (嵌套镶嵌问题DAG)
最新推荐文章于 2020-10-21 10:55:01 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)
描述
有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。