动态规划在有很多的资料,也看了不少,大部分都在说状态转移方程怎么提炼,但是一直都没有弄清楚。模模糊糊的算是理解一点了吧,做下动态规划的笔记。
动态规划,需要建立动态规划表。然后通过这个表,进行dp[i][j]=max(dp[i-1][j],dp[i-1][j-price[i]]+val[i]); 这个公式。
首先,对于动态规划的题目一般有以下几种::
线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等;
区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等;
树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等;
背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶等;
--------------------------------------------------------------------------------------------------------------------------------
下面说个具体的例子吧,例子是九度的1152 题,
北大网络实验室经常有活动需要叫外买,但是每次叫外买的报销经费的总额最大为C元,有N种菜可以点,经过长时间的点菜,网络实验室对于每种菜i都有一个量化的评价分数(表示这个菜可口程度),为Vi,每种菜的价格为Pi,问如何选择各种菜,使得在报销额度范围内能使点到的菜的总评价分数最大。
注意:由于需要营养多样化,每种菜只能点一次。
输入:
输入的第一行有两个整数C(1 <= C <= 1000)和N(1 <= N <= 100),C代表总共能够报销的额度,N>代表能点菜的数目。接下来的N行每行包括两个在1到100之间(包括1和100)的的整数,分别表示菜的价格和菜的评价分数。
样例输入:
40 2
25 30
10 8
样例输出:
38
-------------------------------------------------------------------------------------------------------------------------
其实是0-1背包问题,首先我们可以提炼出下面参数,菜的价格price,菜的评价分数val,拥有的钱C.
那么我们就可以按照动态规划的方程来看看这两个值应该怎么放进去。
price[i] : 第i个菜的价格;
val[i] : 第i个菜的评分;
dp[i][j] :前i个菜的价格放入总钱数为j后的最大评分;
dp[i-1][j] :前i-1个菜的价格放入总钱数为j后的最大评分;
dp[i-1][j-price[i]] : 前i-1个菜的价格放入总钱数为j-price[i]后的最大评分;
然后我们就可以得到:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-price[i]]+val[i]); (j>=price[i])
因为第i个菜的价格比能报销的钱j小,所以我们就考虑两种情况,即前i-1个菜花的钱少于能报销的钱j,或者用j-price[i]的钱去买前i-1道菜,然后再加上第i道菜的分数。我们再把这两种情况的分数取最大值。
代码如下:
#include <stdio.h>
#include <stdlib.h>
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int c,n,i,j;
int price[101],val[101];
int dp[101][1001];
while(scanf("%d %d",&c,&n)!=EOF)
{
for(i=0; i<n; i++)
scanf("%d%d",&price[i],&val[i]);
for(i=0; i<=n; i++)
dp[i][0]=0; //对dp边界清零,也可以使用memset()
for(j=0; j<=c; j++)
dp[0][j]=0;
for(i=1; i<=n; i++)
for(j=1; j<=c; j++)
{
/*如果第i个菜的价格大于所拥有的钱,则买前i个菜得到的最大
分数和买i-1个菜得到的最大分数是相同的*/
if(j<price[i-1])
{
dp[i][j]=dp[i-1][j];
}
else
dp[i][j]=max(dp[i-1][j],dp[i-1][j-price[i-1]]+val[i-1]);
}
printf("%d\n",dp[n][c]);
}
return 0;
}