F. 序列删除
有 𝑛 个数字 𝑎1,𝑎2,…,𝑎𝑛,我们要把除了 𝑎1,𝑎𝑛 之外的其他数字删除,删除一个数字的代价是它乘上它相邻两个还没有被删除的数字的值,请求出最小代价是多少。
输入格式
第一行一个整数 𝑛。
接下来一行 𝑛 个整数 𝑎1,𝑎2,…,𝑎𝑛。
输出格式
一个整数,表示答案。
样例输入
5
5 6 4 2 7
Copy
样例输出
178
Copy
数据规模
对于所有数据,保证 1≤𝑛,𝑎𝑖≤500。
思路:
这个题我们可以看出,这是一个动态规划中的区间dp,然后我们可以先枚举他的区间长度,这个题我们的f[i][j]代表的是删除从i到j的区间中的数,但是我们要枚举区间的左右端点,并且将区间内的每个点都赋值为一个较大的数,然后我们可以根据题目描述推出这个题的状态转移方程,题目中说删去每个点的代价是当前这个点和他左右两个相邻的点的乘积,所以
f[l][r]=min(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r];
然后因为我们的状态设的是删除从i到j之间的数,说以我们在枚举区间长度的时候,要从3开始,到n,最后我们只需要输出f[1][n]的值就可以了
#include <bits/stdc++.h>
using namespace std;
int n;
int a[10005];
int f[505][505];
int main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
for(int len=3;len<=n;len++) {
for(int l=1,r=len;r<=n;l++,r++) {
f[l][r]=0x3f3f3f3f;
for(int k=l+1;k<r;k++) {
f[l][r]=min(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]);
}
}
}
cout<<f[1][n]<<"\n";
return 0;
}
H. [NOIP2012 普及组] 摆花
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 𝑚 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 𝑛 种花,从 1 到 𝑛 标号。为了在门口展出更多种花,规定第 𝑖 种花不能超过 𝑎𝑖 盆,摆花时同一种花放在一起,且不同种类的花需按标号从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
输入格式
第一行包含两个正整数 𝑛 和 𝑚,中间用一个空格隔开。
第二行有 𝑛 个整数,每两个整数之间用一个空格隔开,依次表示 𝑎1,𝑎2,⋯ ,𝑎𝑛。
输出格式
一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 +7 取模的结果。
样例输入
2 4
3 2
Copy
样例输出
2
Copy
数据范围
对于 100% 的数据,保证 0<𝑛≤100,0<𝑚≤100,0≤𝑎𝑖≤100。
思路:
首先这明显又是一道可以用动态规划完成的题目,首先我们确定一下这个题的状态,也就是dp[i][j]表示的是什么,这里dp[i][j]代表的是前i盆花在摆了j盆的情况总数,首先枚举每一种花,然后再枚举每一个摆花的结束点,最后枚举摆的花的盆数,但是还要判断j是否大于等于k的值,如果符合这种条件,那么f[j]=(f[j]+f[j-k])%MOD,注意将MOD定义为一个常数变量,最后只需要,输出f[m]的值就可以了。
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[105];
int f[105];
const int MOD=1e6+7;
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
f[0]=1;
for(int i = 1; i <= n;i++){
for(int j=m;j>=1;j--){
for(int k=1;k<=a[i];k++) {
if(j>=k) {
f[j]=(f[j]+f[j-k])%MOD;
}
}
}
}
cout<<f[m]<<"\n";
return 0;
}
最后:
今天动态规划的讲解与练习就到这里了,我们下期再见。