2.6学习总结10

动态规划

知识点

动态规划是一种解决问题的策略,适用于具有重叠子问题和最优子结构性质的问题。

动态规划的基本思想是将原问题分解为一系列子问题,通过求解子问题的最优解来得到原问题的最优解。在求解子问题时,利用已经求解过的子问题的解来避免重复计算。

动态规划的步骤如下:

  1. 定义状态:将原问题划分为子问题,并定义子问题的状态。
  2. 定义状态转移方程:确定子问题之间的关系,建立状态转移方程。
  3. 初始化:确定初始状态的值。
  4. 确定计算顺序:根据状态转移方程,确定计算子问题的顺序。
  5. 计算最优解:按计算顺序依次计算子问题的最优解。
  6. 求解原问题:根据子问题的最优解,求解原问题的最优解。

动态规划适用于求解最优化问题,例如最长公共子序列问题、0-1背包问题等。它可以显著提高计算效率,减少不必要的重复计算。

台阶问题

假设有n个台阶,现在要求上楼梯的方式数目。每次只能走1步或2步,问有多少种不同的方式可以到达第n个台阶?

我们定义一个dp数组,dp[i]表示到达第i个台阶的不同方式数目。

对于第i个台阶,有两种情况:

  1. 从第i-1个台阶走1步到达第i个台阶;
  2. 从第i-2个台阶走2步到达第i个台阶。

所以可以得到状态转移方程:dp[i] = dp[i-1] + dp[i-2]。

初始条件是:dp[1] = 1,dp[2] = 2。

根据状态转移方程,可以从第3个台阶开始,依次计算dp数组的值,直到计算到第n个台阶的值。

最终,dp[n]就是到达第n个台阶的不同方式数目。

#include <stdio.h>

// 计算n个台阶有多少种上法
int countWays(int n)
{
    // 创建一个长度为n的数组,用来保存计算结果
    int dp[n+1];

    // 基础情况
    dp[1]=1;
    dp[2]=2;

    // 遍历计算每个台阶的上法
    for (int i=3;i<=n;i++)
        dp[i]=dp[i-1]+dp[i-2];

    // 返回最后一个台阶的上法数量
    return dp[n];
}

int main()
{
    int n;
    printf("请输入台阶的数量:");
    scanf("%d", &n);
    printf("共有%d种上法\n", countWays(n));
    return 0;
}
 

P1387 最大正方形

题目描述

在一个 n×m 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。

输入格式

输入文件第一行为两个整数 n,m(1≤n,m≤100),接下来 n 行,每行 m 个数字,用空格隔开,0 或 1。

输出格式

一个整数,最大正方形的边长。

输入输出样例

输入 #1复制

4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1

输出 #1复制

2

我们定义一个dp数组,dp[i][j]表示以节点 i , j 为右下角,可构成的最大正方形的边长。

只有a[i][j]==1时,节点i,j才能作为正方形的右下角;

对于一个已经确定的dp[i][j]=x,它表明包括节点i,j在内向上x个节点,向左x个节点扫过的正方形中所有a值都为1;

状转转移方程:if (a[i][j]==1) dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,m,b=0,a[101][101],dp[101][101]={0};
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
	    {
	        scanf("%d",&a[i][j]);
	        if(a[i][j]==1)
	            dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
	        b=max(b,dp[i][j]);
	    }
	printf("%d",b);
}

 P1934 封印

题目背景

很久以前,魔界大旱,水井全部干涸,温度也越来越高。为了拯救居民,夜叉族国王龙溟希望能打破神魔之井,进入人界“窃取”水灵珠,以修复大地水脉。可是六界之间皆有封印,神魔之井的封印由蜀山控制,并施有封印。龙溟作为魔界王族,习有穿行之术,可任意穿行至任何留有空隙的位置。然而封印不留有任何空隙! 龙溟无奈之下只能强行破除封印。破除封印必然消耗一定的元气。为了寻找水灵珠,龙溟必须减少体力消耗。他可以在破除封印的同时使用越行术。

题目描述

神魔之井的封印共有 n 层,每层封印都有一个坚固值。身为魔族的龙溟单独打破一层封印时需要消耗的元气为该层封印的坚固值和封印总层数 n 的平方的乘积; 但他也可以打破第 i 层到第 j 层之间的所有封印( i<j),总元气消耗为第 i,j 层封印的坚固值之和与第 i,j 层之间所有封印层(包括 i,j 层)的坚固值之和的乘积,但为了不惊动蜀山,第 i,j 层封印的坚固值之和不能大于 t (单独打破可以不遵守)。

输入格式

第一行包含两个正整数 n 和 t。
第二行有 n 个正整数,第 i 个数为 ai​,表示第 i 层封印的坚固值。

输出格式

仅一行,包含一个正整数,表示最小消耗元气。

输入输出样例

输入 #1复制

6 10
8 5 7 9 3 5

输出 #1复制

578

说明/提示

样例解释

先单独打破第一层,再用越行术从第二层直接打破到最后一层。 这样消耗元气 8×62+(5+5)×(5+7+9+3+5)=5788×62+(5+5)×(5+7+9+3+5)=578。

数据范围

对于 10%10% 的数据, n≤10;
对于 50%50% 的数据, n≤100;
对于 70%70% 的数据, n≤500;
对于 100%100% 的数据, n≤1000,ai​(1≤i≤n),t≤20000。

由题目分出两部分

1. "单独打破一层封印时需要消耗的元气为该层封印的坚固值和封印总层数 n 的平方的乘积".

2. "打破第 i 层到第 j 层封印(i<j)的总元气消耗为第 i, j 层封印的坚固值之和与第 i, j 层之间所有封印层(包括第 i, j 层)的坚固值之和的乘积。同时,第 i, j 层封印的坚固值之和必须不大于一个固定值 t" 。

 可由两部分分别推出

1.dp[j]=min(dp[j],dp[j-1]+a[j]*n*n);

2.if(a[j]+a[i]<=t) dp[j]=min(dp[j],dp[i-1]+(a[j]+a[i])*(f[j]-f[i-1]));//i是枚举的点
#include<bits/stdc++.h>
using namespace std;
long long a[1010],sum[1010],dp[1010];
int main()
{
    int n,t,i,j;
    scanf("%d %d",&n,&t);
    for(i=1;i<=n;i++)
    {
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];//前缀和
	}
	for(i=1;i<=n;i++)
	{
		long long m=n*n*a[i]+dp[i-1];
		for(j=1;j<i;j++)
		{
			if(a[i]+a[j]<=t)
			m=min(m,(a[i]+a[j])*(sum[i]-sum[j-1])+dp[j-1]);
		}
		dp[i]=m;
	}
	printf("%lld",dp[n]);
}

 P1115 最大子段和

题目描述

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

输入格式

第一行是一个整数,表示序列的长度 n。

第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai​。

输出格式

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

输入输出样例

输入 #1复制

7
2 -4 3 -1 2 -4 3

输出 #1复制

4

说明/提示

样例 1 解释

选取 [3,5][3,5] 子段 {3,−1,2}{3,−1,2},其和为 44。

数据规模与约定

  • 对于 40%40% 的数据,保证 n≤2×10^3。

  • 对于 100%100% 的数据,保证 1≤n≤2×10^5,−10^4≤ai​≤10^4。

暴力解法 

#include<stdio.h>
int main()
{
	int i,n,k=0;
	long long sum;
	int a[200010]={0};
	scanf("%d",&n);
	k=0;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sum=a[1];
	for(i=1;i<=n;i++)
	{
		if(k>0)
			k+=a[i];
		else
			k=a[i];
		if(k>sum)
			sum=k;
	}
	printf("%lld\n",sum);
}

动态规划

#include<bits/stdc++.h>
using namespace std;
int n,a[200020],dp[200020],i,ans=-2147483647;
// b[i] 表示截止到 i 时,第 i 个数所在的有效序列的元素和。
int main()
{
   scanf("%d",&n);
   for(i=1;i<=n;i++)   
   {
       scanf("%d",&a[i]);
       if(i==1) dp[i]=a[i];
       else dp[i]=max(a[i],dp[i-1]+a[i]);
       ans=max(ans,dp[i]);
   }
   printf("%d",ans);
   return 0;
}

  • 33
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
个人学习golang笔记,从各种教程中总结而来,作为入门参考。目录如下 目录 1. 入门 1 1.1. Hello world 1 1.2. 命令行参数 2 2. 程序结构 3 2.1. 类型 4 2.1.1. 命名类型(named type)与未命名类型(unamed type) 4 2.1.2. 基础类型(underlying type) 4 2.1.3. 可赋值性 5 2.1.4. 类型方法集 6 2.1.5. 类型声明 6 2.2. 变量 8 2.2.1. 变量声明 8 2.2.2. 类型零值 12 2.2.3. 指针 13 2.3. 赋值 17 2.4. 包和文件 17 2.5. 作用域 18 2.6. 语句 19 2.7. 比较运算符 20 2.8. 类型转换 21 2.9. 控制流 23 2.9.1. If 23 2.9.2. Goto 24 2.9.3. For 25 2.9.4. Switch 25 2.9.5. break语句 31 2.9.6. Continue语句 31 3. 基础数据类型 31 3.1. golang类型 31 3.2. Numeric types 32 3.3. 字符串 33 3.3.1. 什么是字符串 33 3.3.2. 字符串底层概念 35 3.3.3. 获取每个字节 38 3.3.4. Rune 39 3.3.5. 字符串的 for range 循环 40 3.3.6. 用字节切片构造字符串 41 3.3.7. 用rune切片构造字符串 42 3.3.8. 字符串的长度 42 3.3.9. 字符串是不可变的 42 3.3.10. UTF8(go圣经) 43 3.4. 常量 45 3.4.1. 常量定义 45 3.4.2. 常量类型 46 3.4.3. Iota 46 4. 组合数据类型 47 4.1. 数组 47 4.1.1. 数组概述 47 4.1.2. 数组的声明 49 4.1.3. 数组的长度 50 4.1.4. 遍历数组 50 4.1.5. 多维数组 51 4.2. 切片 52 4.2.1. 什么是切片 52 4.2.2. 切片概述 55 4.2.3. 创建一个切片 55 4.2.4. 切片遍历 57 4.2.5. 切片的修改 58 4.2.6. 切片的长度和容量 60 4.2.7. 追加切片元素 62 4.2.8. 切片的函数传递 65 4.2.9. 多维切片 66 4.2.10. 内存优化 67 4.2.11. nil slice和empty slice 69 4.2.12. For range 70 4.3. 结构 71 4.3.1. 什么是结构体? 71 4.3.2. 结构体声明 73 4.3.3. 结构体初始化 77 4.3.4. 嵌套结构体(Nested Structs) 81 4.3.5. 匿名字段 82 4.3.6. 导出结构体和字段 84 4.3.7. 结构体相等性(Structs Equality) 85 4.4. 指针类型 86 4.5. 函数 87 4.6. map 87 4.6.1. 什么是map 87 4.6.2. 声明、初始化和make 89 4.6.3. 给 map 添加元素 91 4.6.4. 获取 map 中的元素 91 4.6.5. 删除 map 中的元素 92 4.6.6. 获取 map 的长度 92 4.6.7. Map 的相等性 92 4.6.8. map的排序 92 4.7. 接口 93 4.7.1. 什么是接口? 93 4.7.2. 接口的声明与实现 96 4.7.3. 接口的实际用途 97 4.7.4. 接口的内部表示 99 4.7.5. 空接口 102 4.7.6. 类型断言 105 4.7.7. 类型选择(Type Switch) 109 4.7.8. 实现接口:指针接受者与值接受者 112 4.7.9. 实现多个接口 114 4.7.10. 接口的嵌套 116 4.7.11. 接口的零值 119 4.8. Channel 120 4.9. 类型转换 120 5. 函数 120 5.1. 函数的声明 121 5.2. 一个递归函数的例子( recursive functions) 121 5.3. 多返回值 121 5.4. 命名返回值 121 5.5. 可变函数参数 122 5.6. Defer 123 5.6.1. Defer语句介绍 123 5.6.2. Defer使用场景 128 5.7. 什么是头等(第一类)函数? 130 5.8. 匿名函数 130 5.9. 用户自定义的函数类型 132 5.10. 高阶函数(装饰器?) 133 5.10.1. 把函数作为参数,传递给其它函数 134 5.10.2. 在其它函数中返回函数 134 5.11. 闭包 135 5.12. 头等函数的实际用途 137 6. 微服务创建 140 6.1. 使用net/http创建简单的web server 140 6.2. 读写JSON 144 6.2.1. Marshal go结构到JSON 144 6.2.2. Unmarshalling JSON 到Go结构 146 7. 方法 146 7.1. 什么是方法? 146 7.2. 方法示例 146 7.3. 函数和方法区别 148 7.4. 指针接收器与值接收器 153 7.5. 那么什么时候使用指针接收器,什么时候使用值接收器? 155 7.6. 匿名字段的方法 156 7.7. 在方法中使用值接收器 与 在函数中使用值参数 157 7.8. 在方法中使用指针接收器 与 在函数中使用指针参数 159 7.9. 在非结构体上的方法 161 8. 并发入门 162 8.1. 并发是什么? 162 8.2. 并行是什么? 162 8.3. 从技术上看并发和并行 163 8.4. Go 对并发的支持 164 9. Go 协程 164 9.1. Go 协程是什么? 164 9.2. Go 协程相比于线程的优势 164 9.3. 如何启动一个 Go 协程? 165 9.4. 启动多个 Go 协程 167 10. 信道channel 169 10.1. 什么是信道? 169 10.2. 信道的声明 169 10.3. 通过信道进行发送和接收 169 10.4. 发送与接收默认是阻塞的 170 10.5. 信道的代码示例 170 10.6. 信道的另一个示例 173 10.7. 死锁 174 10.8. 单向信道 175 10.9. 关闭信道和使用 for range 遍历信道 176 11. 缓冲信道和工作池(Buffered Channels and Worker Pools) 179 11.1. 什么是缓冲信道? 179 11.2. 死锁 182 11.3. 长度 vs 容量 183 11.4. WaitGroup 184 11.5. 工作池的实现 186 12. Select 188 12.1. 什么是 select? 188 12.2. 示例 189 12.3. select 的应用 190 12.4. 默认情况 190 12.5. 死锁与默认情况 191 12.6. 随机选取 191 12.7. 这下我懂了:空 select 191 13. 文件读写 191 13.1. GoLang几种读文件方式的比较 197 14. 个人 197 14.1. ++,-- 198 14.2. 逗号 198 14.3. 未使用的变量 199 14.4. Effective go 199 14.4.1. 指针 vs. 值 199 14.5. 可寻址性-map和slice的区别 201 14.5.1. slice 201 14.5.2. map 202 14.6. golang库 203 14.6.1. unicode/utf8包 203 14.6.2. time包 205 14.6.3. Strings包 205 14.6.4. 输入输出 212 14.6.5. 正则处理 224 14.6.6. Golang内建函数 226

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值