[OI - DP] DAG上的动态规划

DAG上的动态规划是学习动态规划的基础。很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。

也就是通常做题时说的“拓扑排序 + DP”的做法

因为最近做的模拟题中经常会出现这样的题

所以我决定学一下并写一篇博客

其实我原来学过只是没学会

我们先从最基础的讲起

首先,图(Graph)描述的是一些个体之间的关系。

图与线性表和二叉树不同的是:这些个体之间既不是前驱后继的顺序关系,也不是祖先后代的层次关系,而是错综复杂的网状关系

而DAG就是图的一种

DAG(Directed Acyclic Graph)的意思是有向无环

所谓有向无环图是指任意一条边有方向,且不存在环路的图

通常,我们把顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。

一个AOV网应该是一个DAG,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

以上就是一些最基础的定义

拓扑排序的实现步骤

1.先把入度为零的点输出并在图中删除(因为在刚开始的一个DAG中入度为零的点可能不止一个,所以拓扑序列并不一定是唯一的)

2.删除与刚刚输出的点有关的边(即使那个点的出度为零)

3.重复上述两步,直至所有点都被输出,或者不再有入度为零的点,后者说明这个图是有环的,所以也可以通过拓扑排序来判断这个图是不是有环图

拓扑排序的代码实现

有两种方法来实现拓扑排序

1.Kahn算法

2.基于DFS实现

——Kahn算法——

Kahn算法的思路就是先使用一个栈用来保存入度为零的点,然后输出栈顶元素并将与栈顶元素有关的边删除,减少与栈顶元素有关的顶点的入度数量并且将入度减少到零的顶点也入栈。

具体的代码实现如下(指针版):

 1 bool Graph_DG::topological_sort(){
 2     cout << "图的拓扑序列为:" << endl;
 3     //栈s用于保存栈为空的顶点下标
 4     stack<int> s;
 5     int i;
 6     ArcNode *temp;
 7     //计算每个顶点的入度,保存在indgree数组中
 8     for (int i = 0; i != this -> vexnum; i++){
 9         temp = this -> arc[i].firstarc;
10         while (temp){
11             ++this -> indegree[temp -> adjvex];
12             temp = temp -> next;
13         }
14     }
15     //把入度为0的顶点入栈
16     for (int i = 0; i != this -> vexnum; i++){
17         if (!indegree[i]){
18             s.push(i);
19         }
20     }
21     //count用于计算输出的顶点个数
22     int count = 0;
23     while (!s.empty()){
24         //如果栈为空,则结束循环
25         i = s.top();
26         s.pop();
27         //保存栈顶元素,并且栈顶元素出栈
28         cout << this -> arc[i].data << " ";
29         temp = this -> arc[i].firstarc;
30         while (temp){
31             if (!(--this -> indegree[temp -> adjvex])){
32                 //如果入度减少到为0,则入栈
33                 s.push(temp -> adjvex);
34             }
35             temp = temp -> next;
36         }
37         ++count;
38     }
39     if (count == this -> vexnum){
40         couot << endl;
41         return true;
42     }
43     cout << "此图有环,无拓扑序列" << endl;
44     return false;//说明这个图有环
45 } 

复杂度为O(V + E)

——基于DFS实现——

推荐学习这个实现方式

因为这个比较好写比较常用

同样地,这个的复杂度也为O(V + E)

借助DFS来完成拓扑排序:

在访问完一个结点之后把它加到当前拓扑序的首部

之所以不是尾部

是因为每次访问完之后,当前的点都是最后的结点

比如1 -> 2, 2 -> 3

因为我们在访问的时候都是从前往后访问的

所以最后访问的数就是最后的数

具体的代码实现如下(数组版):

 1 int c[maxn];
 2 int topo[maxn], t;
 3 bool dfs(int u){
 4     c[u] = -1;//表示正在访问
 5     for (int v = 0; v < n; v++)
 6         if (G[u][v]){
 7             if (c[v] < 0) return false;//存在有向环,失败退出
 8                else if (!c[v] && !dfs(v)) return false; 
 9         }
10     c[u] = 1;
11     topo[--t] = u;
12     return true;
13 }
14 bool toposort(){
15     t = n;
16     memset(c, 0, sizeof(c));
17     for (int u = 0; u < n; u++) 
18         if (!c[u])
19            if (!dis(u)) return false;
20     return true;
21 }

这里用到了一个c数组,c[u] = 0表示从来没有访问过(从来没有调用过dfs(u)),c[u] = 1表示已经访问过,并且还递归访问过它的所有子孙(即dfs(u)曾被调用过,并已返回),c[u] = -1表示正在访问(即递归调用dfs(u)正在栈帧中,尚未返回

Tip:可以用DFS求出有向无环图(DAG)的拓扑排序。如果排序失败,说明该有向图存在有向环,不是DAG

有一道拓扑排序的模板题,超级裸

题目链接

关于DP,因为这篇博客属于DP专题中的一个小专题,所以在这里不详细讲述DP了,只给出一些基础的定义

DP(Dynamic Programming),中文名称叫做动态规划

动态规划是一种用途很广的问题求解方法,它本身并不是一个特定的算法,而是一种思想,一种手段

动态规划的理论性和实践性都比较强,一方面需要理解“状态”、“状态转移”、“最优子结构”、“重叠子问题”等概念

另一方面又需要根据题目的条件灵活设计算法

所以说DP非常玄啊

拓扑排序、DP和DAG上的动态规划(拓扑排序 + DP)的关系

就类似于1 + 1 = 2的关系

所以在做完了准备工作后

我怎么感觉这是拓扑排序专题

开始进入正题

DAG上的动态规划分为两个小题型

1.最长路及其字典序

2.固定终点的最长路和最短路

——最长路及其字典序——

模型:嵌套矩阵问题

有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上的动态规划

矩阵之间的“可嵌套”是显然的二元关系,二元关系可以用图来建模。如果矩阵X可嵌套在矩阵Y里面,那么就从X向Y连一条有向边。这个有向图是无环的,因为一个矩形无法直接或者间接的嵌套在自己内部。

所以,这是一个DAG。这样,我们所求的就是DAG上的最长路径

但我们如何求DAG上不固定起点的最长路径呢

仿照数字三角形的做法,我们设d[i]为从i点出发的最长路径,因为第一步只能走到它的相邻点,所以转移方程很显然的就能写出

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

其中,E为边集,最终答案是所有d(i)中的最大值。

代码如下:

1 int dp(int i){
2     int &ans = d[i];
3     if (ans > 0) return ans;
4     ans = 1;
5     for (int j = 1; j <= n; j++)
6         if (G[i][j]) ans = max(ans, dp(j) + 1);
7     return ans;
8 }

原题中还有一个要求,就是要求字典序最小,我们只需要

转载于:https://www.cnblogs.com/aiyi2000/p/9424954.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Fanuc oi-f plus动态图形是指Fanuc控制系统上用于显示机械运动过程的图形制作和展示功能。它可以实时显示机床零件的加工状态和路径,并通过动画效果展示机械运动的过程。 Fanuc oi-f plus动态图形的制作是通过编程设置相关参数和函数来实现的。首先,需要将机床的运动轨迹和工具的位置信息输入到控制系统中。然后,通过编程语言编写代码,利用图形绘制命令和函数来描述机床的运动轨迹,并根据实际情况进行调整和优化。最后,将编写好的图形程序加载到控制系统中,即可实现动态图形显示功能。 Fanuc oi-f plus动态图形的应用可以帮助机械操作员更直观地了解机床的运动过程和加工状态。通过图形显示,操作员可以清晰地看到零件的加工路径和停留位置,有助于及时发现和纠正加工中可能出现的问题。此外,动态图形还可以提高操作员的生产效率和工作质量,减少人为错误的发生。 总之,Fanuc oi-f plus动态图形是Fanuc控制系统上用于展示机床运动过程的功能。它通过编程设置和图形绘制来实现,能够直观地显示机床的加工状态和路径,提高操作员的工作效率和质量。 ### 回答2: FANUC Oi-F Plus 动态图形是FANUC公司生产的一款先进的数控编程和操作系统。动态图形功能使得机床操作者能够以图形方式实时跟踪机床的工作状态和运动轨迹。 在使用FANUC Oi-F Plus 动态图形功能时,操作者可以通过触摸屏或数控机床的控制面板,以直观的方式监控工件的加工进程。动态图形功能可以实时显示工件的三维模型,并通过不同颜色的标识来表示不同工艺的加工区域,便于操作者快速了解工件的加工情况。 此外,FANUC Oi-F Plus 动态图形功能还可以显示机床的各个轴的实时运动状态,包括速度、位置、加速度等信息。操作者可以通过观察图形界面上的实时曲线图,了解机床的运动轨迹和速度变化,以实时调整加工参数和监控机床的运行状态。 FANUC Oi-F Plus 动态图形功能还配备了丰富的操作指令和功能按钮,方便操作者进行快速操作和控制。操作者可以通过触摸屏进行缩放、平移、旋转等操作,以便更好地观察工件的细节。 总之,FANUC Oi-F Plus 动态图形是一项功能强大的数控系统,能够以图形方式直观地显示机床的工作状态和运动轨迹,提高操作者对机床的控制和监控能力,实现高效的数控加工。 ### 回答3: Fanuc Oi-F Plus动态图形是Fanuc Oi-F Plus数控系统的一个重要功能。它通过显示实时的图形界面,帮助操作员更直观地了解加工过程。 首先,动态图形可以显示工件的加工路径。在加工过程中,操作员可以看到刀具在工件上的运动轨迹,以及加工的具体位置和方向。这样,操作员可以及时发现任何偏差或异常,并进行调整,从而保证加工质量。 其次,动态图形可以实时显示加工状态。操作员可以通过图形界面看到加工进度、剩余加工量和加工时间等信息。这样,操作员可以更好地掌握加工进度,合理安排工作计划,提高生产效率。 此外,动态图形还可以帮助操作员进行工艺优化。操作员可以通过图形界面观察不同工艺参数对加工结果的影响,如切削速度、进给速度等。并且可以通过对比不同工艺方案的效果,选择最优的加工方案,以达到更高的精度和效率。 总之,Fanuc Oi-F Plus动态图形是一个功能强大的工具,它可以帮助操作员更直观地了解加工过程,及时调整并优化加工参数,从而提高加工质量和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值