第九、十一章 动态规划、图论相关知识点总结

来源:互联网 发布:女生香水 知乎 编辑:程序博客网 时间:2024/06/06 11:00

一、动态规划

动态规划的核心是 状态 和 状态转移方程。

解决动态规划的方法一般有两种 

1、递推计算

递推计算的关键是边界和计算顺序

2、记忆化搜索

记忆化搜索不用事先确定计算顺序,所谓的记忆化搜索,就是给每个状态设定一个标志,当这个状态已经被计算过,通过标志判断不再重复计算。

3、DAG上的动态规划

一般的问题可以转化为有向无环图的动态规划,一般分为不知道起点和终点的最长路,已知起点和终点的最长路和最短路。

(1)在未知起点的最长路中,我们可以写出状态转移方程

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

代码可以写为:

int dp(int i){int& ans = d[i];if(ans>0) return ans;ans = 1;for(int j=1;j<=n;j++)if(G[i][j]) ans = max(ans,dp[j]+1);return ans;}
如果有多解,又要保证字典序最小,递归输出。那么:

void print_ans(int i){printf("%d",i);for (int j=1;j<=n;j++) if(G[i][j]&&d[i]==d[j]+1){printf_ans(j);break;}}

(2)固定终点的最长路和最短路(以硬币问题为例)

需要考虑终点不能到达的情况。

使用一个极小的数(特殊值)来表示终点不能到达代码:

int dp(int S)//S为还剩下的价值 {int& ans =d[S]; //硬币数目 if(ans!=-1) return ans;ans = -(1<<30);for (int i=1;j<=n;i++) if(S>=V[i]) ans = max(ans,dp(S-V[i])+1);return ans;}

使用vis[]来表示是否已经访问,代码:


int dp(int S)//S为还剩下的价值 {if(vis[S]) return d[S];vis[S]=1;int& ans =d[S]; //硬币数目 ans = -(1<<30);for (int i=1;j<=n;i++) if(S>=V[i]) ans = max(ans,dp(S-V[i])+1);return ans;}


如果状态复杂,可以使用map来记录状态值,通过if(d.count(S))可以来判断状态是否计算过。


如果既要求最短路,又要求最长路,使用记忆化搜索需要需要写两个,这时可以采用递推的方法。

代码:

minv[0]=maxv[0]=0;for(int i=1;i<=S;i++){minv[i]=INF;maxv[i]=-INF;}for(int i=1;i<=S;i++)for(int j=1;j<=n;j++)if(i>V[j]){maxv[i]= max(maxv[i],maxv[i-V[j]]+1);minv[i]= min(minv[i],minv[i-V[j]]+1);}printf("%d %d\n",minv[S],maxv[S]);//print ansvoid print_ans(int *d ,int S){for(int i =1;i<=n;i++)if(S>=V[i] && d[S]==d[S-V[i]]+1){printf("%d ",i);print_ans(d,S-V[i]);break;}}


实际上,无论我们使用递推计算还是记忆化搜索进行计算,计算的顺序都是从小的状态到大的状态,而大的状态可以根据小的状态的值进行求解。

传统的递推法可以表示为”对于每个状态i,计算f(i)“,这需要对于每个状态i,找到计算f(i)以来的所有状态,而另外一种方法是"对于每个状态i,更新f(i)所影响的状态",称为“刷表法”。

0-1背包问题

for(int i=n;i>=1;i++)//n种物品for(int j=0;j<=C;j++)//j表示的为背包的剩余重量{d[i][j] = (i==n?0:d[i+1][j]);if(j>=V[i]) //表示了背包剩余重量的所有可能,对这个进行了枚举 d[i][j]=max(d[i][j],d[i+1][j-V[i]]+W[i]);//V[i]为第i个物品的体积,W[i]为第i个物品的重量 //d[i][j]表示不选物品i, d[i+1][j-V[i]]+W[i]表示现在了物品i。 } 

采用滚动数组求解

memset(f,0,sizeof(f));for(int i=1;i<=n;i++){scanf("%d%d",&V,&W);for (int j = C;j>=0;j--)if(j>=V) f[j]=max(f[j],f[j-V]+W);}

经典动态规划模型

1、最长上升子序列问题

2、最长公共子序列问题

3、最优矩阵链乘

4、最优三角剖分

树上的动态规划

树上的动态规划,总体而言,就是计算顺序从叶子节点到根,那么需要进行DFS到叶子节点计算状态后返回。适合递归操作。

1、树的最大独立集

2、树的重心(质心)

3、树的最长路径(最远点对)

复杂状态的动态规划

1、最优配对问题

2、TSP问题

3、图的色数

本类问题主要要采用将一个集合表示在数组的下标中,使用了位运算相关知识,具体可以参看第七章的内容

原创粉丝点击