首页 > 其他分享 >动态规划的引入

动态规划的引入

时间:2024-04-19 10:14:00浏览次数:17  
标签:洛谷 int max 样例 record amap 引入 动态 规划

动态规划的引入

引言

可以将动态规划的核心理解为 " 记录 " 和 " 自底向上 "

对于一个问题,如果这个问题能够拆解为同类型的有限个子问题,并且最终的结果能够由这些子问题得到,则可以使用动态规划。
动态规划的实现思想为 " 自底向上 " ,即先求出最小子问题的结果,然后通过这个结果又得到父问题的结果,以此类推,直到得到最终结果。

当然动态规划也可以是 " 记录 " ,即把需要重复计算的值存储下来,等到需要使用的时候就直接用
这样可以减少时间【尤其是在DFS有关的题目中会大大减少耗时】

路径问题

经典路径问题

​ 经典的路径问题给人的感觉更偏重于 " 记录 "
​ 主要是记录每个节点的最大结果,然后用这个结果去求下一个节点的最大结果

洛谷 - P1216 数字三角形 Number Triangles

洛谷 - P1216 数字三角形 Number Triangles

题目描述

​ 观察下面的数字金字塔。

​ 写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。
​ 每一步可以走到左下方的点也可以到达右下方的点。

        7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5 

输入格式

​ 第一个行一个正整数 r ,表示行的数目。
​ 后面每行为这个数字金字塔特定行包含的整数。

输出格式

​ 单独的一行,包含那个可能得到的最大的和。

样例输入 #1

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

样例输出 #1

30

AC CODE

int main()
{
	int n, amap[100][100], flag[100][100];
	cin>>n;
	for(int h=1; h<=n; h++)
		for(int i=1; i<=h; i++)
			cin>>amap[h][i];
	flag[1][1] = amap[1][1];
	for(int h=2; h<=n; h++)
	{
		for(int i=1; i<=h; i++)
		{
			if(i == 1)
				flag[h][i] = flag[h-1][1] + amap[h][i];
			else if(i == h)	
				flag[h][i] = flag[h-1][i-1] + amap[h][i];
			else
				flag[h][i] = amap[h][i] + max(flag[h-1][i], flag[h-1][i-1]);
		}
	}
	int anser = 0;
	for(int h=1; h<=n; h++)
		if(anser < flag[n][h])	anser = flag[n][h];
	cout<<anser<<endl;
	return 0;
} 

需要记录具体方案的问题

记录具体方案的情况需要明白一点:并不需要将方案全部记录下来

即,对于某个节点的最佳方案,并不需要像数组一样记录它的值,是可以记录 它是怎么获得的 或者是 它下一步是去哪里 ,最后再输出方案的时候进行条件判断即可【可以使用递归输出】

洛谷 - P2196 挖地雷

洛谷 - P2196 挖地雷

题目描述

​ 在一个地图上有N个地窖,每个地窖中埋有一定数量的地雷。
​ 同时,给出地窖之间的连接路径。
​ 当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,
​ 然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。
​ 设计一个挖地雷的方案,使某人能挖到最多的地雷。

样例输入 #1

5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1

样例输出 #1

1 3 4 5
27

AC CODE

int n;	// 地窖的数量
int boom_num[Maxsize];	// 每个地窖中地雷的个数
int amap[Maxsize][Maxsize];	// 地窖连接情况
int record[Maxsize];	// 到第 i 个地窖时能获得的最大地雷数
int read_[Maxsize];	// 第 i 个地窖时上一步的地窖 
void FindOrig(int h)
{
	if(read_[h] == -1) 
	{
		cout<<h<<" ";
		return ; 
	}
	FindOrig(read_[h]);
	cout<<h<<" ";
}

int main()
{
	cin>>n;
	for(int h=1; h<=n; h++)	cin>>boom_num[h];
	for(int h=1; h<=n; h++)
		for(int i=h+1; i<=n; i++)
			cin>>amap[h][i];
	record[0] = 0;
	record[1] = boom_num[1];
	for(int h=1; h<=n; h++)	read_[h] = -1;	// 没有地窖能够通往第 h 个地窖 
	for(int h=2; h<=n; h++)
	{
		int tempmax = 0, pos = -1;
		for(int i=1; i<h; i++) 
		{
			if(amap[i][h]) 
			{	// 第 i 个地窖能够达到第 h 个地窖
				if(record[i] > tempmax) 
				{
					tempmax = record[i];
					pos = i;
				}
			}
		}
		record[h] = tempmax + boom_num[h];
		read_[h] = pos; 
	}
	int anspos = 0, temp = 0;
	for(int h=1; h<=n; h++)
	{
		if(record[h] > temp)
		{
			temp = record[h];
			anspos = h;
		}
	}
	FindOrig(anspos);
	cout<<endl<<record[anspos];
	return 0;
} 

与DFS有关的问题

与DFS相关的动态规划可以完全理解为DFS,只是在求每一个节点值的时候将它记录下来

洛谷 - P1434 滑雪

洛谷 - P1434 滑雪

题目描述

​ Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。
​ Michael 想知道在一个区域中最长的滑坡。
​ 区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:

1   2   3   4   5
16  17  18  19  6
15  24  25  20  7
14  23  22  21  8
13  12  11  10  9

​ 一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。
​ 在上面的例子中,一条可行的滑坡为 24-17-16-1(从 24 开始,在 1 结束)。

输入格式

​ 输入的第一行为表示区域的二维数组的行数 R 和列数 C。
​ 下面是 R 行,每行有 C 个数,代表高度(两个数字之间用 1 个空格间隔)。

输出格式

​ 输出区域中最长滑坡的长度。

样例输入 #1

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

样例输出 #1

25

AC CODE

int amap[107][107];	// 输入数据 
int record[107][107];	// h i 点开始,能滑的最大长度 
int m, n, anser = 0;

int DFS(int x, int y)
{
	if(x <= 0 || y <= 0 || x > m || y > n)	// 超出边界
		return 0;
	if(!record[x][y]) 
	{
		int max_around = 0;
		if(amap[x][y] < amap[x-1][y])
			max_around = max(max_around, DFS(x-1, y));
		if(amap[x][y] < amap[x+1][y])
			max_around = max(max_around, DFS(x+1, y));
		if(amap[x][y] < amap[x][y-1])
			max_around = max(max_around, DFS(x, y-1));
		if(amap[x][y] < amap[x][y+1])
			max_around = max(max_around, DFS(x, y+1));
		record[x][y] = 1 + max_around;
	}
	return record[x][y];
} 

int main()
{
	cin>>m>>n;
	for(int h=1; h<=m; h++) 
	{
		for(int i=1; i<=n; i++) 
		{
			cin>>amap[h][i];
			record[h][i] = 0;
		}
	}
	for(int h=1; h<=m; h++)
	{
		for(int i=1; i<=n; i++)
		{
			record[h][i] = DFS(h, i);
			if(anser < record[h][i])
				anser = record[h][i];
		}
	}
	cout<<anser<<endl;
	return 0;
}

0-1背包

0-1背包的核心:a[h][i] = max(a[h][i-1], a[h-item.volume][i-1] + item.value);

即,先记录使用 h-item. volume空间,拿前 i-1 种物品的最大价值结果
则,在使用 h 空间拿前 i 种物品时,要么放入第 i 个物品,要么不放
那么,如果要放入这个物品,则得到的价值为 a[h-item.volume][i-1] + item.value
如果不放,则其价值为 a[h][i-1]

经典0-1背包

洛谷 - P1048 采药

洛谷 - P1048 采药

题目描述

​ 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。
​ 为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。
​ 医师把他带到一个到处都是草药的山洞里对他说:
​ “孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。
​ 我会给你一段时间,在这段时间里,你可以采到一些草药。
​ 如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

​ 如果你是辰辰,你能完成这个任务吗?

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

3

AC CODE

struct item
{
    int gettime;
    int val;
};
int max(int a, int b)
{
    if(a > b) return a;
    return b;
}

int main()
{
    int time, num;
    int record[1007][107]; // 记录最大价值
    item itemlist[107];
    cin>>time>>num;
    for(int h=1; h<=num; h++)
            cin>>itemlist[h].gettime>>itemlist[h].val;
    for(int h=0; h<=num; h++)   record[0][h] = 0;
    for(int h=0; h<=time; h++ ) record[h][0] = 0;
    for(int h=1; h<=time; h++)
    {
        for(int i=1; i<=num; i++)
        {
            if(h >= itemlist[i].gettime)
            {
                int temp = record[h-itemlist[i].gettime][i-1] + itemlist[i].val;
                record[h][i] = max(record[h][i-1], temp);
            }
            else
            {
                record[h][i] = record[h][i-1];
            }
        }
    }
    cout<<record[time][num]<<endl;
    return 0;
}

最大子段和类型

最大子段和

洛谷 - P1115 最大子段和

洛谷 - P1115 最大子段和

题目描述

​ 给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

输入格式

​ 第一行是一个整数,表示序列的长度 n。
​ 第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字。

输出格式

​ 输出一行一个整数表示答案。

样例输入 #1

7
2 -4 3 -1 2 -4 3

样例输出 #1

4

AC CODE

int main()
{
	// record 记录前 i 个数据的最大和 
	int n, array[MaxSize], record[MaxSize], anser = -99999999;	
	cin>>n;
	for(int h=0; h<n; h++) cin>>array[h];
	record[0] = array[0];
	for(int h=1; h<n; h++)
	{
		if(record[h-1] + array[h] > array[h])
			record[h] = record[h-1] + array[h];	
		else
			record[h] = array[h];
		if(anser < record[h])	anser = record[h];
	} 
	if(anser < record[0])	anser = record[0];
	cout<<anser<<endl;
	return 0;
} 

划分数组类型

洛谷 - 1025 数的划分

洛谷 - 1025 数的划分

题目描述

​ 将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
​ 例如:n=7,k=3,下面三种分法被认为是相同的。

​ 1,1,5;
​ 1,5,1;
​ 5,1,1;

​ 问有多少种不同的分法。

输入格式

​ n,k (6< n < 200,2 < k < 6)

输出格式

​ 1 个整数,即不同的分法。

样例输入 #1

7 3

样例输出 #1

4

AC CODE

import java.util.Scanner;

class dpMap {
    public int num;
    public int piece;
    public int[][] piece_map;

    public dpMap(int n, int p) {
        this.num = n;
        this.piece = p;
        this.piece_map = new int[n + 1][p + 1];
        for (int h = 0; h <= n; h++) {
            this.piece_map[h][1] = 1; // 把h个1分成1份的方案数为1
        }
        for (int h = 0; h <= p; h++) {
            this.piece_map[h][h] = 1; // 把h个1分成h份的方案数为1
        }
        for (int h = 0; h <= n; h++) {
            this.piece_map[h][0] = 0;
        }
        for (int h = 0; h <= p; h++) {
            this.piece_map[0][h] = 0;
        }
    }
    
    public void dpPieceMap() {
        for (int h = 2; h <= this.num; h++) {
            for (int i = 2; i <= this.piece; i++) {
                if (h == i) {
                    this.piece_map[h][i] = 1;
                } else if (h < i) {
                    this.piece_map[h][i] = 0;
                } else {
                    for (int j = 1; j <= i; j++) {
                        this.piece_map[h][i] += this.piece_map[h - i][j];
                    }
                }
            }
        }
    }
}

public class LuoGu_P1025 {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            int num = scanner.nextInt(), piece = scanner.nextInt();
            dpMap dpmap = new dpMap(num, piece);
            dpmap.dpPieceMap();
            for (int h = 0; h <= num; h++) {
                for (int i = 0; i <= piece; i++) {
                    System.out.print(dpmap.piece_map[h][i] + " ");
                }
                System.out.println();
            }
            System.out.println(dpmap.piece_map[num][piece]);
        }
    }
}

动态策略:
当数多于盘子时:piece_map[h][i] = piece_map_cannull[h-i][i];
piece_map_cannull[h-i][i] = Σpiece_map[h][j];(j∈[1,i])

标签:洛谷,int,max,样例,record,amap,引入,动态,规划
From: https://www.cnblogs.com/Muhuai/p/18145184

相关文章

  • @RefreshScope实现动态刷新配置原理
    1@RefreshScope介绍在介绍@RefreshScope之前,先介绍作用域的概念:在springioc中存在5种BeanScope,即:singleton:每一个SpringIoC容器都拥有唯一的一个实例对象(默认作用域)prototype:一个BeanDefinition对应多个对象实例,每次取出的都是不同的对象request:每一个HTTP请求都有自己的B......
  • vue引入字体icon
    这里我用的是阿里图标库1.2.3.4.在vue的assets文件中增加一个font文件把解压后的文件复制进去,并在mian.js中引入iconfont.css5.1.使用,复制以下代码在页面中使用<h1>欢迎<iclass="iconfont">&#xe67c;</i></h1>5.1.2使用,复制一下代码在页面中使用<h1>欢迎<icla......
  • java动态代理模式
    Java动态代理模式是Java编程语言中的一种设计模式,它提供了一种在运行时动态创建代理对象的方式。这个模式主要用于实现AOP(面向切面编程)的概念,允许开发者在不修改原有业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理、权限验证等。在Java中,动态代理模式主要依赖于java.l......
  • Trino418版本动态加载catalog不需要重启集群修改思路及实现2
       原来没事的时候改了一个这样的功能,当时也没有仔细研究,后来也没继续弄。详细可以参考 https://www.cnblogs.com/liuzx8888/p/17635913.html当时有1个问题:新增数据源需要每一个节点都去调取API注册,这样非常麻烦,最近闲下来又研究了一下,在原先的基础上做了一些改造。具体流......
  • 洛谷题单指南-动态规划1-P1115 最大子段和
    原题链接:https://www.luogu.com.cn/problem/P1115题意解读:计算最大字段和,典型dp问题。解题思路:设a[]表示所有整数,f[i]表示以第i个数结束的最大字段和当f[i-1]>=0时,f[i]=f[i-1]+a[i]否则,f[i]=a[i]因此,递归式为f[i]=max(a[i],f[i-1]+a[i])注意整数可能为负,ans初始......
  • Pyecharts制作动态GDP柱状图
    学习使用pyecharts制作动态柱状图使用csv模块进行csv数据文件处理importcsvfrompyecharts.chartsimportBar,Timelinefrompyecharts.optionsimport*frompyecharts.globalsimportThemeTypedefdealCSVFile():"""读取处理csv数据文件:retu......
  • 洛谷题单指南-动态规划1-P1434 [SHOI2002] 滑雪
    原题链接:https://www.luogu.com.cn/problem/P1434题意解读:计算能滑行的最长距离。解题思路:设dp(i,j)表示从i,j可以滑行的最大距离对于4个方向i,j可以到达的点,ni,nj,如果可以滑过去(ni,ni所在点高度更低)则dp(i,j)=max(dp(i,j),1+dp(ni,nj))为了便于搜索4个方向的各条路径,......
  • EAS_DEP添加动态控件,在代码中获取DEP扩展控件
    1.在编辑界面onload的方法前置事件添加脚本//把动态控件传递到代码中varcomponents=newjava.util.HashMap();components.put("prmtassureAmountAccount",pluginCtx.getKDBizPromptBox("prmtassureAmountAccount"));components.put("prmtassureInterestAccount",......
  • 洛谷题单指南-动态规划1-P2196 [NOIP1996 提高组] 挖地雷
    原题链接:https://www.luogu.com.cn/problem/P2196题意解读:求一条路径,使得所有点地雷数之和最大。解题思路:1、DFS先建图,再从1~n点分别进行一次DFS,记录过程中地雷总数最大的,并且同时记录遍历的顺序。数据量不大,直接就可以过。100分代码:#include<bits/stdc++.h>usingnamespa......
  • Autoware.universe规划模块-番外
    在仿真测试过程中发现,当前车道中心线上附近存在障碍物时,车辆不会触发avoidance场景。为了保证车辆完成绕障动作,需要进行一下修改:1.修改enable_force_avoidance_for_stopped_vehicle:enable_force_avoidance_for_stopped_vehicle:true2.修改threshold_distance_object_is_......