本次将介绍(可能不是一次性的噢):0-1背包问题、硬币问题、最长递增子序列、正方形搜索。
硬币问题(Coin Changing Problem):
题意:先有面值为元的n种硬币,求支付m元时所需硬币的最少枚数。各面值的硬币可以重复使用任意次。
输入 m n
输出 输出所需硬币的最少枚数,占1行
限制 , , 各面值均不相同,其中必须包含1。
这个问题可以通过动态规划法来求最优解。最重要的操作就是设动态变量,状态转移方程式。
C[n+1];//C[j]表示第i中硬币的面值
T[n+1][m+1];//T[i][j]表示使用第0到第i中硬币支付j元时所需的最少硬币数
设我们考虑到了C[i]硬币,面对的是需要支付j元。这里就要展开讨论了,该枚硬币“放”or“不放”。
“不放”:
“放”:
最后取最小:
列代表硬币值(1,2,3)、行代表支付面值(0,1,2,3,4,5)
0 | 1 | 2 | 3 | 4 | 5 | |
1 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
2 | 0 | 1 | 1 | 2 | 2 | 3 |
3 | 0 | 1 | 1 | 1 | 2 | 2 |
#include<iostream>
#include<algorithm>
#define MAX 100
#define INF (1<<21)
using namespace std;
int C[MAX];//储存硬币面值
int main(){
int m,n;//支付m元的n种硬币
cin>>m>>n;
C[0]=0;
for(int i=1;i<=n;i++)
cin>>C[i];
sort(C+1,C+n+1);//保证硬币从小到大
//T[i][j]表示前i种硬币数支付j元所需最小硬币数
int T[n+1][m+1];
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
//支付数为0时硬币数为0
if(j==0)
T[i][j]=0;
//否则为取min赋值∞
else
T[i][j]=INF;
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
//当C[i]放不进去时
if(j<C[i])
T[i][j]=T[i-1][j];
//*容量够考虑放不放(可以重复放)
else
T[i][j]=min(T[i-1][j],T[i][j-C[i]]+1);
}
}
cout<<T[n][m]<<endl;
return 0;
}
从上表来看,我们没必要给每一种面值都记录最优枚数,因此支付j元时的最少枚数可以作为一维数组元素T[j],
可得: 。
// 面值、种数、硬币值
int getTheNumberOfCoin(int m,int n,int C[]){
int T[m+1];
for(int i=0;i<=m;i++)
T[i]=INF;
T[0]=0;
for(int i=0;i<=n;i++){
for(int i=0;i<=n;i++){
for(int j=0;j+C[i]<=m;j++)
T[j+C[i]]=min(T[j+C[i]],T[j]+1);
}
}
return T[m];
}
本题的性质为可重复的放入类似多重背包问题 。