[TOC]
动态规划(一)背包问题
注意:
1、边界问题
2、初始化
3、核心的地方在状态转移,比较偏数学
4、背包和贪心没有算法模版
复杂度的计算:
状态数量 * 转移的计算量
01 背包问题
题目描述
有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的价值是 Wi。求解将哪些物品装入背包可使价值总和最大。每件物品只能用一次,只能选择放与不放进背包。
状态表示与转移状态方程
二维数组代码表示
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++) // 循环后面超过一行代码都记得打大括号
{
f[i][j] = f[i-1][j];
if (j >= v[i]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0; //don't forget
}
一维代码表示(滚动数组)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i++)
for (int j = m; j >= v[i]; j--) //思考下这里的构造,为什么要从大到小
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
cout << f[m] << endl;
return 0;
}
完全背包问题
题目描述
有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的价值是 Wi。求解将哪些物品装入背包可使价值总和最大。每件物品有无限件。
状态转移方程
朴素思想代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = 0; j <= m; j++)
for(int k = 0; k * v[i] <= j; k++ )
f[i][j] = max( f[i][j] , f[i-1][j - k*v[i]] + k * w[i]);
cout << f[n][m] << endl;
return 0;
}
二维数组优化代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = 0 ; j <= m; j++)
{
f[i][j] = f[i-1][j];
if (j >= v[i]) f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
再次优化到一维数组
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = v[i] ; j <= m; j++)
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
cout << f[m] << endl;
return 0;
}
多重背包问题
题目描述
有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的价值是 Wi。求解将哪些物品装入背包可使价值总和最大 。第i件物品最多有Si件可用。
状态转移方程
暴力写法
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
for(int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for(int k = 0; k <= s[i] && k * v[i] <= j; k++) //注意还要满足k*v[i] <= j
f[i][j] = max(f[i][j], f[i-1][j - k*v[i]] + k*w[i]);
cout << f[n][m] << endl;
return 0;
}
优化方法(二进制)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 25000;
int n, m;
int v[N], w[N], s[N];
int f[N];
int main()
{
cin >> n >> m;
//for(int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
//如果先定义v[i]后面再操作的话,新的v[i]会覆盖后面的v[i]导致值不一样;
int count = 0;
for(int i = 1; i <= n; i++)
{
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while(k <= s)
{
count++;
v[count] = k * a;
w[count] = k * b;
s -= k;
k = k * 2;
}
if( s > 0 )
{
count++;
v[count] = s * a;
w[count] = s * b;
}
}
for(int i = 1; i <= count; i++)
for(int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
分组背包问题
题目描述
有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的价值是 Wi。求解将哪些物品装入背包可使价值总和最大。这些物品被划为K组,每组中的物品互相冲突,只能选一件。
状态转移方程
二维数组代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int s[N];
int v[N][N], w[N][N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> s[i];
for(int j = 1; j <= s[i]; j++ ) cin >> v[i][j] >> w[i][j];
}
for(int i = 1; i <= n; i++)
for(int j = 0; j <= m; j++)
{
f[i][j] = f[i - 1][j];
for(int k = 1; k <= s[i]; k++)
if(j >= v[i][k]) f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]);
}
cout << f[n][m] << endl;
return 0;
}
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int s[N];
int v[N][N], w[N][N];
//int f[N][N];
int f[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> s[i];
for(int j = 1; j <= s[i]; j++ ) cin >> v[i][j] >> w[i][j];
}
for(int i = 1; i <= n; i++)
for(int j = m; j >= 0; j--)
for(int k = 1; k <= s[i]; k++)
{
//f[i][j] = f[i - 1][j];
//if( j >= v[i][k]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i][k]] + w[i][k]);
if (j >= v[i][k]) f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
}
cout << f[m] << endl;
return 0;
}