DAG 上的动态规划(白书)

举报
Linux猿 发表于 2021/08/04 23:10:51 2021/08/04
【摘要】 题目链接~~>       有向无环图(DAG,Directed Acyclic Graph)上的动态规划是学习动态规划的基础。很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。 一、矩形嵌套 题目描述:        有n个矩形,每个矩形可以...

题目链接~~>

      有向无环图(DAG,Directed Acyclic Graph)上的动态规划是学习动态规划的基础。很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。

一、矩形嵌套

题目描述:

       有n个矩形,每个矩形可以用两个整数a,b描述,表示它的长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d,或者b<c,a<d(相当于把矩形X旋转90°)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)内。你的任务是选出尽可能多的矩形排成一行。使得除了最后一个之外,每个矩形都可以嵌套在下一个矩形内。

解题思路:

       如何求DAG中不固定起点的最长路径呢?仿照数字三角形的做法,设d(i)表示从节点i出发的最长路长度,应该如何写状态方程呢?第一步只能走到它的相邻点,因此:

           d(i) = max { d(j) + 1 | (i,j)-> E }

其中E为边集,最终答案为d(i).那如果要求输出字典序最小的最长路径呢?那么必须找到第一个最长的路径的值然后递归输出。

代码:


  
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #include<algorithm>
  5. #include<iostream>
  6. using namespace std ;
  7. const int MX = 1000 + 10 ;
  8. int n ;
  9. int G[MX][MX],dp[MX] ;
  10. struct node
  11. {
  12. int x,y ;
  13. }T[MX] ;
  14. void buildGraph() // 建图
  15. {
  16. memset(G,0,sizeof(G)) ;
  17. for(int i=0 ;i<n ;i++)
  18. for(int j=0 ;j<n ;j++)
  19. if(T[i].x>T[j].x&&T[i].y>T[j].y)
  20. G[i][j]=1 ;
  21. }
  22. int DAG(int x) // 记忆化求解
  23. {
  24. int& ans = dp[x] ;
  25. if(ans > 0) return ans ;
  26. ans=1 ;
  27. for(int i=0 ;i<n ;i++)
  28. if(G[x][i])
  29. {
  30. int mx=DAG(i)+1 ;
  31. ans = ans > mx ? ans : mx ;
  32. }
  33. return ans ;
  34. }
  35. void print(int x) // 打印路径
  36. {
  37. printf("%d ",x) ;
  38. for(int i=0 ;i<n ;i++)
  39. if(G[x][i]&&dp[x]==dp[i]+1)
  40. {
  41. print(i) ;
  42. break ;
  43. }
  44. }
  45. int main()
  46. {
  47. int Tx ;
  48. scanf("%d",&Tx) ;
  49. while(Tx--)
  50. {
  51. scanf("%d",&n) ;
  52. for(int i=0 ;i<n ;i++)
  53. {
  54. scanf("%d%d",&T[i].x,&T[i].y) ;
  55. if(T[i].x>T[i].y)
  56. swap(T[i].x,T[i].y) ;
  57. }
  58. int ans=1 ;
  59. buildGraph() ;
  60. memset(dp,-1,sizeof(dp)) ;
  61. for(int i=0 ;i<n ;i++)
  62. {
  63. int mx=DAG(i) ;
  64. ans= mx > ans ? mx : ans ;
  65. }
  66. for(int i=0 ;i<n ;i++)// 寻找第一个点
  67. if(dp[i]==ans)
  68. {
  69. printf("%d\n",ans) ;
  70. print(i) ;
  71. break ;
  72. }
  73. }
  74. return 0 ;
  75. }

二、硬币问题

题目描述:

              有n种硬币,面值分别为V1,V2...,Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。0 <= n <= 100, 0 <= S <= 10000, 1 <= Vi <= S。

解题思路:

             本题的本质还是DAG上的路径问题。我们把每种面值看作一个点,表示"还需要凑足的面值",则初始状态为S,目标状态为0。若当前的状态i,每使用一个硬币j,状态便转移到i-Vj。这个模型和嵌套矩形一题类似,但也有些明显的不同之处:上题并没有确定路径的起点和终点(可以把任意矩形放在第一个和最后一个),而本题的起点必须是S,终点必须是0。把终点固定之后"最短路"才是有意义的。在嵌套矩形中,最短序列显然是空(如果不允许空的话,就是单个矩形,不管怎样都是平凡的),而本题的最短路径却不是那么容易确定的。             
        接下来考虑"硬币问题"。注意到最长路和最短路的求法是类似的,下面只考虑最长路。由于终点固定,d(i)的确切含义变为"从节点i出发到节点0的最长路径长度"。

代码:


  
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<iostream>
  4. #define INF 1<<30
  5. #define maxn 100+10
  6. using namespace std ;
  7. int V[maxn],n;
  8. int min[maxn],max[maxn];
  9. inline int Min(int a,int b){return a<b?a:b;}
  10. inline int Max(int a,int b){return a>b?a:b;}
  11. //打印可行的方案
  12. void print_ans(int* d,int S)
  13. {
  14. for(int i=1;i<=n;i++)
  15. {
  16. if(S>=V[i] && d[S]==d[S-V[i]]+1)
  17. {
  18. printf("%d ",V[i]);
  19. print_ans(d,S-V[i]);
  20. break;
  21. }
  22. }
  23. }
  24. int main()
  25. {
  26. int S;
  27. while(~scanf("%d%d",&S,&n)) //输入面值S和面值的种数n
  28. {
  29. for(int i=1;i<=n;i++)
  30. scanf("%d",&V[i]);
  31. max[0]=0; min[0]=0;
  32. for(int i=1;i<=S;i++)
  33. min[i]=INF,max[i]=-INF;
  34. //递推实现
  35. for(int i=1;i<=S;i++)
  36. for(int j=1;j<=n;j++)
  37. if(i>=V[j])
  38. {
  39. min[i]=Min(min[i],min[i-V[j]]+1);
  40. max[i]=Max(max[i],max[i-V[j]]+1);
  41. }
  42. print_ans(min,S);
  43. printf(" min\n");
  44. print_ans(max,S);
  45. printf(" max\n");
  46. printf("min:%d max:%d\n",min[S],max[S]);
  47. }
  48. return 0;
  49. }



 

文章来源: blog.csdn.net,作者:Linux猿,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/nyist_zxp/article/details/37689755

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。