版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目:
点击跳转
输入:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出:
2200
解题:
初看这道题,数据就 m<60, 感觉dfs就能直接搜索过,结果TLE,仔细再想一想会发现时间复杂度达到了O(m^m) 。
TLE代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100;
struct node {
int v, p, q;
}a[N];
int n, m;
int vis[N];
int ans;
void dfs ( int price_sum, int sum , int pre ) { //pre 存储选的前一个的乘积值
if ( price_sum > n ) {
ans = max( ans, sum-pre);
return ;
}
for ( int i = 1; i <= m; i ++ ) {
if ( !vis[i] && vis[a[i].q] ) { //如果它是附件,判断主件是否被选
vis[i] = 1;
int t = a[i].v*a[i].p;
dfs ( price_sum + a[i].v, sum + t, t );
vis[i] = 0;
}
}
}
int main() {
scanf("%d%d", &n, &m);
for ( int i = 1; i <= m; i ++ ) {
scanf("%d%d%d", &a[i].v, &a[i].p, &a[i].q);
}
memset( vis, 0, sizeof( vis));
vis[0] = 1; // 先将0置为被选,便于后续判断中主件直接被选
dfs (0, 0, 0 );
printf("%d\n", ans);
return 0;
}
所以在这里我们换种思路:
- 题意:每个主件最多只有两个附件,且附件没有附件
- 因此我们可以通过转化,把原问题转化为 01背包问题来解决。在用 01背包之前我们需要进行预处理,把 每一种物品归类(把每一个主件和它的附件看作一类物品)。
- 当取某件物品时,我们只需要从以下五种方案中取最大的那种方案:
- 不取
- 只取主件
- 取主件+附件1
- 取主件+附件2
- 既主件+附件1+附件2
- 时间复杂度 O(m*n)
所以得到 状态转移方程:
dp[i][j] = max (
dp[i-1,j],
dp[i-1][j-a[i][0]] + a[i][0]*b[i][0],
dp[i-1][j-a[i][0]-a[i][1]] + a[i,0]*b[i][0] + a[i][1]*b[i][1],
dp[i-1][j-a[i][0]-a[i][2]] + a[i][0]*b[i][0] + a[i][2]*b[i][2],
dp[i-1][j-a[i][0]-a[i][1]-a[i][2]] + a[i][0]*b[i][0] + a[i][1]*b[i][1] + a[i][2]*b[i][2]
);
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 65;
int w[N][3], v[N][3], dp[N][3205];
int n, m, c, p, q;
int main() {
cin >> n >> m;
n /= 10; // n/10 便于节省时间与空间
for ( int i = 1; i <= m; i ++) {
cin >> c >> p >> q;
c /= 10; // 与上述同理
if ( q == 0 ) { //预处理存储 (i,0) 存储主件,
w[i][q] = c; v[i][q] = c*p;
}
else if ( w[q][1] == 0 ) { //(i,1)存储第一个附件
w[q][1] = c; v[q][1] = c*p;
}
else { // (i,2) 存储第二个附件
w[q][2] = c; v[q][2] = c*p;
}
}
memset( dp, 0, sizeof(dp) );
for( int i = 1; i <= m; i ++ ) {
for ( int j = 1; j <= n; j ++ ) {
dp[i][j] = dp[i-1][j]; // 不取
if ( j >= w[i][0] ) { // 只取主件
dp[i][j] = max( dp[i][j], dp[i-1][j-w[i][0]] + v[i][0]);
}
if ( j >= w[i][0] + w[i][1] ) { // 取主件+附件1
dp[i][j] = max( dp[i][j], dp[i-1][j-w[i][0]-w[i][1]] + v[i][0] + v[i][1]);
}
if ( j >= w[i][0] + w[i][2] ) { // 取主件+附件2
dp[i][j] = max( dp[i][j], dp[i-1][j-w[i][0]-w[i][2]] + v[i][0] + v[i][2]);
}
if ( j >= w[i][0] + w[i][1] + w[i][2] ) { //取主件+附件1+附件2
dp[i][j] = max( dp[i][j], dp[i-1][j-w[i][0]-w[i][1]-w[i][2]] + v[i][0] + v[i][1]+ v[i][2]);
}
}
}
cout << dp[m][n]*10 << endl;
return 0;
}