算法设计与分析课-实验-动态规划
算法设计与分析课
第一题
矩阵连乘问题:
分析:该问题求矩阵连乘积最少乘次数,对于两个矩阵A(i行j列)和B(j行n列),他们相乘的乘次数为ijn,对于两个矩阵只有它们的列和行相等才能相乘。
用数组A接受矩阵的行列,对于一个矩阵An,其行为A[n-1],列为A[n],题目中给了Ai与Ai+1是可以相乘的。用数组M表示矩阵链相乘的最优解,例如M[i][j]表示从矩阵i到矩阵j的矩阵链相乘的最小计算次数。
利用动态规划求解,对于每一个矩阵链,其中的矩阵不断相乘,最后剩下两个矩阵相乘得到最后的矩阵,记它们在k处断开。但是k的值不确定,需要计算,所以动态规划就得到了解决公式。
如果i=j,则M[i][j]=0。
如果i<j,则M[i][j]等于min{M[i][k]+M[k+1][j]+Ai-1 * Ak * Aj}。
解决代码:
#include<iostream>
#include<cstring>
#define maxn 1000+5
#define inf 0x3f3f3f3f
using namespace std;
//利用动态规划求解
//用数组A接受矩阵的行列,对于一个矩阵An,其行为A[n-1],列为A[n],题目中给了Ai与Ai+1是可以相乘的
//用数组M表示矩阵链相乘的最优解,例如M[i][j]表示从矩阵i到矩阵j的矩阵链相乘的最小计算次数
//分析矩阵链的相乘,乘到最后只剩下两个矩阵相乘然后得到最终的矩阵,那么可以假设该矩阵链最后的相乘是从k处断开的
//但是k的值不确定,需要计算,所以动态规划就得到了解决公式
//如果i=j,则M[i][j]=0,如果i<j,则M[i][j]等于min{M[i][k]+M[k+1][j]+pi-1*pk*pj}
//其中p为矩阵的行列
int M[maxn][maxn];//记录矩阵链相乘的最优解
int S[maxn][maxn];//记录矩阵链相乘的断开点
int A[maxn];//记录矩阵
int n;//矩阵个数
void MatrixMultiply(int A[], int n, int M[][maxn], int S[maxn][maxn]);
void PrintWay(int S[][maxn], int l, int r);
int main()
{
int n;
cin>>n;
for(int i=0; i<=n; i++) cin>>A[i];
//memset(S, -1, sizeof(S));
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
S[i][j]=-1;
MatrixMultiply(A, n, M, S);
cout<<M[1][n]<<endl;
//cout<<S[1][1]<<endl;
PrintWay(S, 1, n);
return 0;
}
void MatrixMultiply(int A[], int n, int M[][maxn], int S[maxn][maxn])
{
//memset(M, inf, sizeof(M));
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
M[i][j]=inf;
for(int i=1; i<=n; i++) M[i][i]=0;
for(int len=2; len<=n; ++len)//矩阵链的长度
{
for(int i=1; i<=n-len+1; ++i)
{
int j=i+len-1;//最后一个矩阵的列
/*不给数组M设置初值的写法,初始认为k为i
M[i][j]=M[i][i]+M[i+1][j]+A[i-1]*A[i]*A[j];//可以不写M[i][j],因为该元素值为0
S[i][j]=i;
for(int k=i+1; k<j; ++k)
{
if(M[i][k]+M[k+1][j]+A[i-1]*A[k]*A[j]<M[i][j])
{
M[i][j]=M[i][k]+M[k+1][j]+A[i-1]*A[k]*A[j];
S[i][j]=k;
}
}
*/
for(int k=i; k<j; ++k)
{
if(M[i][k]+M[k+1][j]+A[i-1]*A[k]*A[j]<M[i][j])
{
M[i][j]=M[i][k]+M[k+1][j]+A[i-1]*A[k]*A[j];
S[i][j]=k;
}
}
}
}
}
void PrintWay(int S[][maxn], int l, int r)
{
if(S[l][r]!=-1)
{
PrintWay(S, l, S[l][r]);
PrintWay(S, S[l][r]+1, r);
cout<<"Multiply A"<<l<<","<<S[l][r]<<" and A"<<S[l][r]+1<<","<<r<<endl;
}
else
{
return ;
}
}
第二题
背包问题即将n个不同的物品放入一个容量为c的背包中,要求这些物品组成的价值最大。
利用动态规划进行求解,定义一个dp[i][j],其含义为当前背包容量为j的条件下前i个物品的最优组合,然后分析递推公式。
首先对于我们当前选中的一个物品,它只有放入背包和不放入背包两种可能。
1、j<w[i],则该物品重量大于背包容量,不能放入背包,所以dp[i][j]=dp[i-1][j],也即
此时(dp[i][j])的最大总价值和只从前i-1个物品选择装入背包的最大总价值是一样的。
2、j>w[i],则该物品重量小于背包容量,可以放入背包,但是放入背包之后其占用了背包的容量,却并不一定可以使背包的总价值达到最大,所以要进行判定,判定公式为dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])。对两种情况取max,前者表示该物品不放入背包,背包总价值等于只从前i-1个物品中进行选择放入背包的总价值,后者表示该物品装入背包,背包总价值等于在背包容量为j-w[i]时只从前i-1个物品中进行选择的总价值加上该物品的价值(背包容量为j-w[i]因为该物品的重量也占用了背包容量)。
代码:
#include<iostream>
#include<cstring>
#define maxn 1000+5
using namespace std;
//0-1背包问题
//利用动态规划进行求解
//定义一个dp[i][j],其含义为当前背包重量为j的条件下前i个物品的最优组合,然后寻找递推公式
//对于当前选中的一个商品,只有两种可能
//1、装不下,j<w[i],那么dp[i][j]=dp[i-1][j],也即此时背包总价值和只从前i-1个物品中选择的总价值是一样的
//2、装得下,j>=w[i],但是可以选择不装,因为装了也不一定使背包总价值变大,那么dp[i][j]=max{dp[i-1][j], dp[i-1][j-w[i]]+v[i]}
int n;//物品数
int w[maxn];//物品重量
int v[maxn];//物品价值
int s[maxn];//物品是否装入背包 1->装入
int c;//背包容量
int maxWeight;
int dp[maxn][maxn];
void getMax(int n, int c, int w[], int v[], int dp[][maxn]);
void findWay(int i, int j, int dp[][maxn], int s[]);
void printWay(int s[], int n);
int main()
{
maxWeight=0;
cin>>n; cin>>c;
for(int i=1; i<=n; ++i) cin>>w[i];
for(int i=1; i<=n; ++i) cin>>v[i];
for(int i=1; i<=n; ++i)
for(int j=1; j<=c; ++j)
dp[i][j]=0;
memset(s, 0, sizeof(s));
getMax(n, c, w, v, dp);
findWay(n, c, dp, s);
printWay(s, n);
return 0;
}
void getMax(int n, int c, int w[], int v[], int dp[][maxn])
{
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=c; ++j)
{
if(j<w[i])
{
dp[i][j]=dp[i-1][j];
}
else
{
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
}
}
}
}
//思路:对于dp[i][j],如果它等于dp[i-1][j],说明没有选择i,如果满足递推公式的第二个情况,说明选择了i
void findWay(int i, int j, int dp[][maxn], int s[])
{
if(i>=0)
{
if(dp[i][j]==dp[i-1][j])
{
s[i]=0;
findWay(i-1, j, dp, s);
}
else if(j>=w[i] && dp[i][j]==dp[i-1][j-w[i]]+v[i])
{
s[i]=1;
findWay(i-1, j-w[i], dp, s);
}
}
}
void printWay(int s[], int n)
{
int ans=0;
for(int i=1; i<=n; ++i)
{
if(s[i])
{
ans+=v[i];
}
}
cout<<ans<<endl;
for(int i=1; i<=n; ++i)
{
if(s[i])
{
cout<<"x"<<i<<" ";
}
}
}
findWay函数用来得到最优解选择装入了哪些物品,思路为:对于dp[i][j],如果它等于dp[i-1][j],说明没有选择i,如果满足递推公式的第二个情况,说明选择了i,然后递归求解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律