金明的预算方案(01背包)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43054397/article/details/101109069

题目:

点击跳转
lc
在这里插入图片描述
输入:

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43054397/article/details/101109069