dijkstra(单源最短路径):

策略:

设集合S存放已被访问的顶点,然后执行n次下面的两个步骤

(1)每次从集合V-S(未访问节点)选择与起点s的距离最小的一个顶点(记为u),访问并加入集合S(已访问节点)。

(2)之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。

复制代码
  1. constint MAXV = 1000;//最大顶点数
  2. constint INF = 0x3f3f3f3f;//设INF为一个很大的数
  3.  
  4. int n, G[MAXV][MAXV];//n为定点数,MAXV为最大定点数
  5. int d[MAXV];//起点到各点的最短路径长度
  6. bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问。初值均为false
  7.  
  8. void Dijkstra(int s){//s为起点
  9. fill(d, d + MAXV, INF);//fill函数将整个d数组赋为INF(慎用memset)
  10. d[s] = 0;//起点s到达自身的距离为0
  11. for(int i = 0; i < n; i++){//循环n次
  12. int u = -1, MIN = INF;//u使d[u]最小,MIN存放该最小的d[u]
  13. for(int j = 0; j < n; j++){//
  14. if(vis[j] == false && d[j] < MIN){
  15. u = j;
  16. MIN = d[j];
  17. }
  18. }
  19. //找不到小于INF的d[u],说明剩下的顶点和起点s不连通
  20. if(u == -1) return;
  21. vis[u] = true;//标记u为已访问
  22. for(int v = 0; v < n; v++){//从u出发能达到的所有顶点v,更新d[v]
  23. //如果v未访问&&u能到达v&&以u为中介点可以使d[v]更优
  24. if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
  25. d[v] = d[u] + G[u][v];//优化d[v]
  26. }
  27. }
  28. }
  29. }

 

图的DFS(求联通分量数):

①邻接矩阵

复制代码
  1. const int MAXV = 1000;//最大顶点数
  2. const int INF = 0x3f3f3f3f;//设INF为一个很大的数
  3.  
  4. int n, G[MAXV][MAXV];//n为定点数,MAXV为最大定点数
  5. bool vis[MAXV] = {false};//如果顶点i已被访问,则vis[i]==true。初值为false
  6.  
  7. void DFS(int u, int depth) {//u为当前访问的顶点标号,depth为深度
  8. vis[u] = true;//设置u已被访问
  9. //如果需要对u进行一些操作,可以在这里进行
  10. //下面对所有从u出发能到达的分支进行枚举
  11. for (int v = 0; v < n; v++) {//对每个顶点v
  12. if (vis[v] == false && G[u][v] != INF) {//如果v未被访问,且u可达v
  13. DFS(v, depth + 1);//访问v,深度加1
  14. }
  15. }
  16. }
  17.  
  18. void DFSTrace(){//遍历图G
  19. for(int u = 0; u < n; u++){//对每个顶点u
  20. //int count=0//用来统计联通分量数
  21. if(vis[u] == false){//如果u未被访问
  22. DFS(u, 1);//访问u和所在的连通块,1表示初始为1层
  23. //count++;
  24. }
  25. }
  26. }

②邻接表

复制代码
  1. vector<int> Adj[MAXV];//图G的邻接表
  2. int n;//n为定点数,MAXV为最大顶点数
  3. bool vis[MAXV] = { false };//如果顶点i已被访问,则vis[i]==true,初值为fales
  4.  
  5. void DFS(int u, int depth) {//u为当前访问的顶点标号,depth为深度
  6. vis[u] = true;//设置u已被访问
  7. /*如果需要对u进行一些操作,可以在此处进行*/
  8. for (int i = 0; i < Adj[u].size(); i++) {//对从u出发可以到达的所有顶线v
  9. int v = Adj[u][i];
  10. if (vis[v] == false) {//如果v未被访问
  11. DFS(v, depth + 1);//访问v,深度加1
  12. }
  13. }
  14. }
  15.  
  16. void DFSTrave() {//遍历图G
  17. for (int u = 0; u < n; u++) {//对每个顶点u
  18. if (vis[u] == false) {//如果u未被访问
  19. DFS(u, 1);//访问u和u所在的连通块,1表示初始为第一层
  20. }
  21. }
  22. }

图的BFS

复制代码
  1. int n, G[MAXV][MAXV];//n为顶点数,MAXV为最大顶点数
  2. bool inq[MAXV] = { false };//若顶点i曾入过队列,则inq[i]==true,初值为false
  3.  
  4. void BFS(int u) {//遍历u所在的连通块
  5. queue<int> q;//定义队列q
  6. q.push(u);//将初始点u入队
  7. inq[u] = true;//设置u已被加入过队列
  8. while (!q.empty()) {//只要队列非空
  9. int u = q.front();//取出队首元素
  10. q.pop();//将队首元素出队
  11. for (int v = 0; v < n; v++) {
  12. //如果u的邻接点v未曾加入过队列
  13. if (inq[v] == false && G[u][v] != INF) {
  14. q.push(v);//将v入队
  15. inq[v] = true;//标记v已被加入过队列
  16. }
  17. }
  18. }
  19. }
  20.  
  21. void BFSTrave() {//遍历图G
  22. for (int u = 0; u < n; u++) {//枚举所有顶点
  23. if (inq[u] == false) {//如果u未曾加入过队列
  24. BFS(u);//遍历u所在的连通块
  25. }
  26. }
  27. }

01背包:

递推:

dp[i][v]表示前i件物品(1≤i≤n,0≤v≤V)恰好装入容量为v的背包中所能获得的最大价值。

考虑对i件物品的选择策略:

1、不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v的背包中所能获得 的最大价值,也即dp[i-1][v]

2、放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v-w[i]的背包中所能获得的最大价值,也即dp[i-1][v-w[i]]+c[i]

dp[i][v]=max{dp[i1][v],dp[i1][vw[i]]+c[i]}

(1in,w[i]vV)

滚动数组:

dp[j]=max{dp[j],dp[jw[i]]+c[j]}

复制代码
  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4.  
  5. const int maxn = 100;//物品最大件数
  6. const int maxv = 1000;//V的上限
  7.  
  8. int w[maxn], c[maxn], dp[maxv];
  9. int main() {
  10. int n, V;
  11. cin >> n >> V;
  12. for (int i = 0; i < n; i++) {
  13. cin >> c[i];
  14. }
  15. //边界
  16. for (int v = 0; v < V; v++) {
  17. dp[v] = 0;
  18. }
  19. for (int i = 1; i <= n; i++) {
  20. for (int v = V; v >= w[i]; v--) {
  21. //状态转移方程
  22. dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
  23. }
  24. }
  25. //寻找dp[0...V]中最大的即为答案
  26. int max = 0;
  27. for (int v = 0; v <= V; v++) {
  28. if (dp[V] > max) {
  29. max = dp[v];
  30. }
  31. }
  32. cout << max;
  33. }

完全背包

区别于01背包,每种物品都有无穷件。

dp[i][v]表示前i件物品(1≤i≤n,0≤v≤V)恰好装入容量为v的背包中所能获得的最大价值。

递推:

考虑对i件物品的选择策略:

1、不放第i件物品,那么dp[i][v]=dp[i-1][v],这步和01背包是一样的。

2、放第i件物品,不同于01背包,而是转移到dp[i][v-w[i]],这是因为每种物品可以放任意件(注意有容量限制,因此还是有限的),放了第i件物品后还可以继续放第i件物品,直到第二维的v-w[i]无法保持大于等于0为止。

dp[i][v]=max{dp[i1][v],dp[i][vw[i]]+c[i]}

(1in,w[i]vV)

边界:dp[0][v]=0(0vV)

状态转移方程:

dp[j]=max{dp[j],dp[jw[i]]+c[j]}

形成一维后和01背包完全相同,唯一的区别在于这里v的枚举顺序是正向枚举。

最小生成树:

prim算法:

复制代码
  1. using namespace std;
  2.  
  3. const int MAXV = 1000;//最大顶点数
  4. const int INF = 100000000;//设INF为一个很大的数
  5.  
  6. int n, G[MAXV][MAXV];//n为顶点数,MAXV为最大顶点数
  7. int d[MAXV];//顶点与集合S的最短距离
  8. bool vis[MAXV] = { false };//标记数组,vis[i]==true表示已访问。初值均为false
  9.  
  10. int prim() {//默认0号为初始点,函数返回最小生成树的边权之和
  11. fill(d, d + MAXV, INF);//fill函数将整个d数组赋为INF(慎用memset)
  12. d[0] = 0;//只有0号顶点到集合S的距离为0,其余全为INF
  13. int ans = 0;//存放最小生成树的边权之和
  14. for (int i = 0; i < n; i++) {//循环n次
  15. int u = -1, MIN = INF;//u使d[u]最小,MIN存放该最小的d[u]
  16. for (int j = 0; j < n; j++) {//找到未访问的顶点中的d[]最小的
  17. if (vis[j] == false && d[j] < MIN) {
  18. u = j;
  19. MIN = d[j];
  20. }
  21. }
  22. //找不到小于INF的d[u],则剩下的顶点和集合S不连通
  23. if (u == -1)return -1;
  24. vis[u] = true;//标记u为已访问
  25. ans += d[u];//将与集合S距离最小的边加入最小生成树
  26. for (int v = 0; v < n; v++) {
  27. //v未访问&&u能到达v&&以u为中介点可以使v离集合S更近
  28. if (vis[v] == false && G[u][v] != INF && G[u][v] < d[v]) {
  29. d[v] = G[u][v];//将G[u][v]赋值给d[v]
  30. }
  31. }
  32. }
  33. return ans;//返回最小生成树的边权之和
  34. }