算法--动态规划(Dynamic Programming)

目录

一 动态规划概述

二 动态规划在图论中应用场景

三 c实例

1. **最短路径问题(Dijkstra算法)**:

2. **最小生成树问题(Kruskal算法)**:


一 动态规划概述

动态规划(Dynamic Programming,简称DP)是一种用于解决具有重叠子问题和最优子结构特性的问题的优化方法。动态规划通过将原问题分解为相互重叠的子问题,并将子问题的解存储在一个表格中,以便在后续计算中重复使用。这种方法可以显著减少计算复杂度,特别是对于具有指数级别复杂度的递归问题。

以下是一个使用动态规划解决的经典问题:0-1背包问题。

问题描述:给定一组物品,每个物品有一定的价值和重量。现在有一个背包,它有一定的承重能力。我们的目标是在不超过背包承重的前提下,将物品放入背包以获得最大的价值。

C实现实例:


#include<stdio.h>
#include <stdlib.h>

int max(int a, int b) {
    return a > b ? a : b;
}

int knapsack(int W, int wt[], int val[], int n) {
    int i, w;
    int **K = (int **)malloc((n + 1) * sizeof(int *));
    for (i = 0; i <= n; i++) {
        K[i] = (int *)malloc((W + 1) * sizeof(int));
    }

    for (i = 0; i <= n; i++) {
        for (w = 0; w <= W; w++) {
            if (i == 0 || w == 0) {
                K[i][w] = 0;
            } else if (wt[i - 1] <= w) {
                K[i][w] = max(val[i - 1] + K[i - 1][w - wt[i - 1]], K[i - 1][w]);
            } else {
                K[i][w] = K[i - 1][w];
            }
        }
    }

    int result = K[n][W];

    for (i = 0; i <= n; i++) {
        free(K[i]);
    }
    free(K);

    return result;
}

int main() {
    int val[] = {60, 100, 120};
    int wt[] = {10, 20, 30};
    int W = 50;
    int n = sizeof(val) / sizeof(val[0]);

    printf("最大价值为: %d\n", knapsack(W, wt, val, n));

    return 0;
}

在这个例子中,我们使用动态规划解决了一个0-1背包问题。我们定义了一个二维数组`K`来存储子问题的解。`K[i][w]`表示在前`i`个物品中,背包容量为`w`时能够获得的最大价值。我们根据状态转移方程来填充`K`数组,并在填充过程中利用之前计算的子问题解。最后,`K[n][W]`就是我们要求的最大价值。

这个实现展示了动态规划在解决优化问题中的强大能力。通过将问题分解为子问题并存储子问题的解,我们可以显著减少计算复杂度,从而在有限的时间内找到最优解。

需要注意的是,动态规划适用于具有重叠子问题和最优子结构特性的问题。对于其他类型的问题,可能需要采用其他优化方法或算法。在实际应用中,需要根据问题的具体特点和约束条件来选择合适的算法和数据结构。

二 动态规划在图论中应用场景

动态规划在图论中的许多问题中表现最佳,尤其是在处理具有重叠子问题和最优子结构的问题时。以下是一些具体的应用场景:

1. **最短路径问题**:动态规划可以用来寻找图中两个顶点之间的最短路径。例如,Dijkstra算法和Floyd-Warshall算法都是基于动态规划的求解最短路径问题的方法。

2. **最小生成树问题**:动态规划可以用来寻找无向图的最小生成树,这是一种在网络设计、电路设计等领域中常见的问题。Kruskal算法和Prim算法都是基于动态规划的求解最小生成树问题的方法。

3. **最长公共子序列问题**:在生物信息学中,动态规划可以用来寻找两个序列的最长公共子序列。这个问题可以转换为在图中寻找两个顶点之间的最长路径问题。

4. **背包问题**:动态规划可以用来解决0/1背包问题,即在给定一组物品和一个容量有限的背包的情况下,如何选择物品以使得背包中物品的总价值最大化。这个问题可以转换为在图中寻找一条路径,使得路径上所有顶点的权值之和最大,同时满足路径上顶点的数量限制。

5. **最长递增子序列问题**:在生物信息学中,动态规划可以用来寻找一个序列的最长递增子序列。这个问题可以转换为在图中寻找一条路径,使得路径上所有顶点的值按照一定的顺序递增。

总之,动态规划在图论中的许多问题中都有广泛的应用,它可以帮助我们有效地解决这些问题,找到最优解。

三 c实例

以下是使用C语言实现的一些动态规划在图论中的应用实例:

1. **最短路径问题(Dijkstra算法)**:


#include<stdio.h>
#include<limits.h>

#define INF INT_MAX
#define N 5

int minDistance(int dist[], int sptSet[]) {
    int min = INF, min_index = -1;

    for (int v = 0; v < N; v++) {
        if (sptSet[v] == 0 && dist[v] <= min) {
            min = dist[v];
            min_index = v;
        }
    }

    return min_index;
}

void dijkstra(int graph[N][N], int src) {
    int dist[N];
    int sptSet[N];

    for (int i = 0; i < N; i++) {
        dist[i] = INF;
        sptSet[i] = 0;
    }

    dist[src] = 0;

    for (int count = 0; count < N - 1; count++) {
        int u = minDistance(dist, sptSet);
        sptSet[u] = 1;

        for (int v = 0; v < N; v++) {
            if (!sptSet[v] && graph[u][v] && dist[u] != INF && dist[u] + graph[u][v]< dist[v]) {
                dist[v] = dist[u] + graph[u][v];
            }
        }
    }

    printf("Vertex\tDistance from Source\n");
    for (int i = 0; i < N; i++) {
        printf("%d\t\t%d\n", i, dist[i]);
    }
}

int main() {
    int graph[N][N] = {
        {0, 10, 15, 20, 0},
        {0, 0, 35, 25, 0},
        {0, 0, 0, 30, 0},
        {0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0}
    };

    dijkstra(graph, 0);

    return 0;
}

2. **最小生成树问题(Kruskal算法)**:


#include<stdio.h>
#include<stdlib.h>

#define N 5

typedef struct Edge {
    int src, dest, weight;
} Edge;

typedef struct Graph {
    int V, E;
    Edge* edge;
} Graph;

Graph* createGraph(int V, int E) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->V = V;
    graph->E = E;
    graph->edge = (Edge*)malloc(E * sizeof(Edge));
    return graph;
}

int find(int parent[], int i) {
    if (parent[i] == -1)
        return i;
    return find(parent, parent[i]);
}

void union_(int parent[], int x, int y) {
    int xset = find(parent, x);
    int yset = find(parent, y);
    parent[xset] = yset;
}

int isCycle(Graph* graph) {
    int parent[graph->V];
    memset(parent, -1, sizeof(parent));

    for (int i = 0; i< graph->E; i++) {
        int x = find(parent, graph->edge[i].src);
        int y = find(parent, graph->edge[i].dest);

        if (x == y)
            return 1;

        union_(parent, x, y);
    }
    return 0;
}

void kruskal(Graph* graph) {
    int result[graph->V], i, j;
    memset(result, 0, sizeof(result));

    for (i = 0; i< graph->E; i++) {
        int u = graph->edge[i].src;
        int v = graph->edge[i].dest;
        int w = graph->edge[i].weight;

        if (result[u] == 0 && result[v] == 0) {
            result[u] = result[v] = 1;
            printf("Edge %d-%d with weight %d\n", u, v, w);
        }
    }
}

int main() {
    int V = 5;
    int E = 8;
    Graph* graph = createGraph(V, E);

    graph->edge[0].src = 0;
    graph->edge[0].dest = 1;
    graph->edge[0].weight = 10;

    graph->edge[1].src = 0;
    graph->edge[1].dest = 2;
    graph->edge[1].weight = 6;

    graph->edge[2].src = 0;
    graph->edge[2].dest = 3;
    graph->edge[2].weight = 5;

    graph->edge[3].src = 1;
    graph->edge[3].dest = 3;
    graph->edge[3].weight = 15;

    graph->edge[4].src = 2;
    graph->edge[4].dest = 3;
    graph->edge[4].weight = 4;

    graph->edge[5].src = 1;
    graph->edge[5].dest = 2;
    graph->edge[5].weight = 5;

    graph->edge[6].src = 2;
    graph->edge[6].dest = 4;
    graph->edge[6].weight = 2;

    graph->edge[7].src = 3;
    graph->edge[7].dest = 4;
    graph->edge[7].weight = 9;

    if (isCycle(graph))
        printf("Graph contains cycle\n");
    else
        kruskal(graph);

    return 0;
}

这些实例展示了动态规划在解决图论中的最短路径问题和最小生成树问题中的应用。通过使用动态规划,我们可以在较短的时间内找到从源顶点到其他所有顶点的最短路径,以及在无向图中找到最小生成树。这些算法在实际应用中具有广泛的应用价值,例如在网络设计、电路设计、物流优化等领域。需要注意的是,动态规划适用于具有重叠子问题和最优子结构特性的问题。对于其他类型的问题,可能需要采用其他优化方法或算法。在实际应用中,需要根据问题的具体特点和约束条件来选择合适的算法和数据结构。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值