先说说动态规划吧,最重要的就是找转移方程(虽然我开始感觉自己没找,都是自己想的,但是回头看,只不过是脑子里找的,但是当时不认为那个是转移方程,多刷刷就会了)
背包问题这里就不讲了,网上找找就好啦
简单提一下优化,就是背包的d[i][j]可以优化为d[j],因为每一次都是递推,把所有的和所有累加的放进去,一个一维数组足矣。
讲完全背包之前,先讲一个完全背包退化版的问题,对理解完全背包很有帮助哦
在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法。请你编程序计算出共有多少种兑法。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1284
这个题其实用暴力也是可以的,但是为了学动态规划,就得用动态规划的思想想想
假如只有1分硬币,就是从第一个递推到第n个,假如只有2分、3分同理,转移方程是d[j] = d[j] + d[j - i]。
#include <stdio.h>
int a[40000];
int main()
{
int i = 0;
int j = 0;
a[0] = 1;
for(i = 1;i <= 3;i++)
for(j = i;j <= 40000;j++)
a[j] += a[j -i];
while(scanf("%d",&i) != EOF)
printf("%d\n",a[i]);
return 0;
}
下来讲讲完全背包问题
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
先说说最简单的方法把,把完全背包问题退化为背包问题就行了(^_^)
这种方法就是说,把每一个物品都乘上几次(肯定比最大载重小),让一个物品变成多个物品,然后按背包问题的思路就行了。
虽然这种方法也行,也是O(n * v),但是在常数意义上,就高很多了,所以还是用一般方法好点。
背包问题的转移方程:d[i][j] = max(d[i - 1][j],d[i - 1][j - v[i]] + w[j]);
意思就是每一个后面的都是d[i - 1][j]或者d[i - 1][j - v[i]] + w[j],然后无限下推,分成一个个自问题。
完全背包是每一个后面的都是前面所有的加上(自己*k)所有可能的,
#include "iostream"
#include "cstdio"
#include "cstring"
#include "cstdlib"
#include "cmath"
using namespace std;
int M,N; //M是最大容量,M <= 200
int W[300],C[300];
int qq[200]; //从前到后遍历,该对应容量的最大价值
int main()
{
memset(qq,0,sizeof(qq));
memset(W,0,sizeof(W));
memset(C,0,sizeof(C));
scanf("%d%d",&M,&N);
int i = 0;
for(i = 0;i < N;i++)
scanf("%d%d",&W[i],&C[i]);
for(i = 0;i < N;i++)
{
if(qq[W[i]] <C[i])
qq[W[i]] = C[i];
int j = 0;
for(j = 0;j <= M - W[i];j++)
{
int abc = j + W[i]; //记录是否小于200
while(abc <= M)
{
//qq[j + W[i]] < qq[j] + c[i];
if(qq[abc] < (qq[abc-W[i]] + C[i]))
qq[abc] = qq[abc-W[i]] + C[i];
abc += W[i];
}
}
}
int max = 0;
for(i = 0;i <= M;i++)
if(qq[i] > max)
max = qq[i];
cout << max << endl;
return 0;
}