动态规划1——电路布线

动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

简单来说,动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法。

动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算

1.把电路布线问题作为动态规划算法的第一个问题是不太合适的,因为这个问题看着比较复杂,难以看懂,而且抽象成代码比较困难,但是实际需要我去讲这个问题,所以就把电路布线问题作为我学习动态规划的第一个问题。

看问题:

这是书上给的内容,看完这个题目和分析,是不是感觉很懵,这个问题我们需要get到如下几点就够了。

1.每个端点都有端点对应,一个上端点对应一个下端点,我们可以看成从上端点1到10按照顺序向下连线。因此我们可以使用一维数组来存储这个电路板,其中上端点为数组下标,下端点为数组的值。(这些限制了解法1,解法2大于解法1)

2.该问题就是求解在电路网格中最多有几个互不相交的线,其实就是求这些布线中最多选几条线使其不相交。

下面进行动态规划思路的思考。

动态规划的问题思考分为5个步骤

1.状态表示:就是思考dp数组是什么,代表什么意思,我们可以构建一个二维的dp数组,dp[i][j]表示在当前上下端点的连接之下,我们原来网格中最多有几个互不相交的电线。如dp[7][5]代表从上端点7到下端点5这条线左边有几条不相交线路

2:状态转移方程:第二张图中给了我们状态转移方程,简要分析如下:
上端点为1时,dp[1][j],当j<a[1](上端点1对应的下端点a[1])时,dp[1][j]=0,当j>=a[1]时,dp[1][j]=1

上端点i大于1时
若j小于a[i]时,就是指下端点还没有到我们当前该上端点对应的下端点上,因为不包括i到a[i]这条线,就是说删除i点对结果没有影响,所以就有dp[i][j]=dp[i-1][j],而dp[i-1]的所有值已算出

若j大于等于a[i],说明包括了i到a[i]这条线,此时需要比较dp[i-1][a[i]-1]+1与dp[i-1][j]的关系,dp[i][j]=max{dp[i-1][a[i]-1]+1,dp[i-1][j]},若前者比较大,dp[i-1][a[i]-1]+1表示当前的原dp数组中加入这条线,后者表示该线不加入当前确定的最大数组中,而是选择以这个dp数组的前一个节点作为最大值向后面更新。

3.初始化

根据状态转移方程来初始化,对这个二维dp数组初始化,我们最好把dp等于0的情况全部初始化为0,防止对数组中未初始化的值进行访问。

4.构建dp表

5.返回值

这个返回值就是dp[n][n],就是二维数组的最后一个值,因为二维dp数组的更新大的值一定会沉到后面,因此dp[n][n]就是我们的返回值。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#define N 100
int main()
{
	int i, j, n;
	//创建dp表
	int a[N], dp[N][N];
	printf("请输入端点个数:");
	scanf("%d", &n);
	printf("请输入各端点对应的下端点:");
	for (i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}

	//初始化dp表
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j <= n; j++)
		{
			if (i == 0 || j == 0)
				dp[i][j] = 0;
		}
	}
	//填表
	for (i = 1; i <= n; i++) {//上端点为1的情况 
		if (i < a[1]) dp[1][i] = 0;
		else dp[1][i] = 1;
	}
	
	for (i = 2; i <= n; i++) {//其他情况 
		for (j = 1; j <= n; j++) {
			if (j < a[i]) {//小于対应线的坐标 
				dp[i][j] = dp[i - 1][j];
			}
			else {//大于等于 
				//printf("%d \n", dp[i - 1][a[i] - 1]);
				if (dp[i - 1][j] > dp[i - 1][a[i] - 1] + 1) {//更新dp最大值 
					dp[i][j] = dp[i - 1][j];
				}
				else
					dp[i][j] = dp[i - 1][a[i] - 1] + 1;
			}
		}
	}
	// 返回结果
	printf("%d\n", dp[n][n]);
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j <= n; j++)
		{
			printf("%d ", dp[i][j]);
		}
		printf("\n");
	}
	return 0;
}

该题目还有解法2,分析可以知道,这个题目其实是求最大递增子序列题目的子题目,因此可以用求最大递增子序列的方法求解该题目。这个题目后面再讲,该解法代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#define N 100
int max(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int n;
	int a[N];
	printf("请输入端点个数:");
	scanf("%d", &n);
	printf("请输入各端点对应的下端点:");
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
	}
 // 创建dp表
	int dp[N];

	//初始化dp表
	for (int i = 0; i < n; i++)
	{
		dp[i] = 1;
	}
 // 填表
	int ret = 0;
	for (int i = 1; i < n; i++)
	{
		for (int j = 0; j < i; j++)
		{
			if (a[i] > a[j])
				dp[i] = max(dp[j] + 1, dp[i]);
		}
		if (dp[i] > ret)
			ret = dp[i];
	}
 // 返回结果
	printf("%d ", ret);
	return 0;
}

参考博客:

算法设计——电路布线问题(动态规划)_电路布线问题动态规划算法-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值