蓝桥杯-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;
}
视频学习
视频名称及链接:
动态规划入门
解决多阶段问题,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个解决。
动态规划算法通常用于求解具有某种最优性质的问题,在这类问题中,可能会有许多可行解。每一个解对于于一个值,我们希望找到具有最优值的解。
动态规划法的基本思路:
我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
动态规划的基本概念:
● 阶段:把所给问题的求解过程适当地分成若干个相互联系的阶段,以便于求解。
● 状态:状态表示每个阶段开始面临的自然状况或客观条件
● 决策:一个状态变到另一个状态,怎么变过去,就是一个决策
● 状态转移方程:把一个状态转移到下一个状态对应的式子,本阶段状态往往是上一阶段的状态和上一阶段的决策的结果。
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(i−1,j),dp(i,j−1)+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;
}