蓝桥杯-2.28

蓝桥杯-2.28

代码学习

圆的面积

思路:注意浮点数的输入,定值可以再头文件先声明

#include <iostream>
using namespace std;
#define PI 3.14159265358979323
int main()
{
    int r;
    double s;
    cin >> r;
    printf("%.7f",PI * r * r);
    return 0;
}

视频学习

视频名称及链接:

2019年蓝桥杯训练营(C++) 12.2动态规划入门讲解

动态规划入门

解决多阶段问题,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个解决。

动态规划算法通常用于求解具有某种最优性质的问题,在这类问题中,可能会有许多可行解。每一个解对于于一个值,我们希望找到具有最优值的解。

动态规划法的基本思路:

我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。

动态规划的基本概念:

● 阶段:把所给问题的求解过程适当地分成若干个相互联系的阶段,以便于求解。

● 状态:状态表示每个阶段开始面临的自然状况或客观条件

● 决策:一个状态变到另一个状态,怎么变过去,就是一个决策

● 状态转移方程:把一个状态转移到下一个状态对应的式子,本阶段状态往往是上一阶段的状态和上一阶段的决策的结果。
F ( i + 1 ) = T ( f ( i ) , u ( i ) ) F(i + 1) = T(f(i),u(i)) F(i+1)=T(f(i),u(i))
第i+1段的状态由第i段的状态f(i)和决策u(i)来决定

● 策略:各个阶段决策确定后,整个问题的决策序列就构成了一个策略。

动态规划必须满足最优化原理(当前到这一步是最优的,才能保证后面有可能是最优的,也就是一个最优策略的子策略也是最优的)与无后效性(某个状态给定后,这个阶段以后过程的发展不受以前各个状态的影响)

例题-蒜头君回家

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7Edlr5J-1614526583758)(C:\Users\费泽涛\Desktop\图片\屏幕截图 2021-02-28 142955.png)]

我们把走到一个点看做一个状态,走到一个点只有俩种方式,一个是从下面走到该点,一种是从左边走到该点。所以从哪个点走到(i, j)就是一个决策,用dp(i, j)来代表走到点(i, j)一共花费的最少体力。

可得状态转移方程:
d p ( i , j ) = m i n ( d p ( i − 1 , j ) , d p ( i , j − 1 ) + a i j ) dp(i, j) = min(dp(i - 1, j), dp(i, j - 1) + aij) dp(i,j)=min(dp(i1,j),dp(i,j1)+aij)
转移的顺序一定要保证这个点可以接到前面所有该有的点的转移,还有注意边界点。

#include <iostream>
#include <algorithm>
using namespace std;
int a[110][110];//绘制地图
int dp[110][110];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> a[i][j];//读入地图
        }
    }
    dp[1][1] = 0;//起点位置体力为0
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i == 1 && j == 1)//起点
            {
                continue;
            }
            else if (i == 1)//处理边界点
            {
                dp[i][j] = dp[i][j - 1] + a[i][j];
            }
            else if (j == 1)//处理边界点
            {
                dp[i][j] = dp[i - 1][j] + a[i][j];
            }
            else
            {
                dp[i][j] = min(dp[i][j-1], dp[i - 1][j]) + a[i][j];
            }
        }
    }
    cout << dp[n][n] << endl;
    return 0;
}
例题-弹簧板(加强)

有一个小球掉落在一串连续的弹簧板上,小球落到某一个弹簧板后,会被弹到某一个地点,直到小球被弹到弹簧板以外的地方。假设有 n 个连续的弹簧板,每个弹簧板占一个单位距离,a[i] 代表代表第 i 个弹簧板会把小球向前弹 a[i] 个距离。比如位置 1 的弹簧能让小球前进 2 个距离到达位置 3。如果小球落到某个弹簧板后,经过一系列弹跳会被弹出弹簧板,那么小球就能从这个弹簧板弹出来。现在希望你计算出小球从任意一个弹簧板落下,最多会被弹多少次后,才会弹出弹簧板。

输入格式:

第一个行输入一个 n 代表一共有 n(1≤n≤100000) 个弹簧板。第二行输入 n 个数字,中间用空格分开。第 i 个数字 a**i(1≤a**i≤30) 代表第 i 个弹簧板可以让小球移动的距离。

思路:用DP,根据动态规划,我们知道落到第i个弹簧弹出边界的次数设为dp[i],那么它一定等于第i+a[i]位置弹出边界的次数再加上1(第i跳到第i+a[i]算一次),所以状态转移方程为:
d p [ i ] = d p [ i + a [ i ] ] + 1 dp[i]=dp[i+a[i]]+1 dp[i]=dp[i+a[i]]+1
代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 100010;
int a[maxn], dp[maxn];
int main()
{
    int n;
    cin >> n;
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    int ans = 0;
    for (int i = n; i >= 1; --i)
    {
        dp[i] = dp[i + a[i]] + 1;
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;
    return 0;
}
对应练习

数组分组

一个长度为 n 的数组 a,我们可以把它分成任意组,每一组是一段连续的区间。比如数组1,2,3,4,5 可以分成 (1,2),(3,4,5) 两组。每个分组都有一个权值,这个权值就是分组里面每个数的乘积对1000 取模的结果。对于数组 a 的一个分组方案,总权值就是每个分组的权值和。那么对于数组 a,分组以后最大的权值和是多少?

输入格式

输入第一张一个整数 n(1≤n≤1000)。
接下来一行 n 个整数,表示数组 a,数组中每个元素都小于等于 100。

输出格式

数组最大的分组权值和。

样例输入:

7
52 26 1 36 72 48 43
样例输出:

1596

#include <iostream>
using namespace std;
int a[1005];
int pre[1005][1005];
int dp[1005];
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i]; 
	}
	for (int i = 1; i <= n; i++)
	{
		pre[i][i] = a[i];
		for (int j = i + 1; j <= n; j++)
		{
			pre[i][j] = pre[i][j - 1] * a[j] % 1000;//算出每种分组的权值 
		}
	}
	dp[0] = 0;
	dp[1] = a[1];
	for (int i = 2; i <= n; i++)
	{
		for (int j = 0; j < i; j++)
		{
			dp[i] = max(dp[i], dp[j] + pre[j + 1][i]);//求每种权值的最大值 
		}
	}
	cout << dp[n] << endl; 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值