0
点赞
收藏
分享

微信扫一扫

背包问题(01、完全、多重)

颜路在路上 2022-03-11 阅读 48

目录

01背包问题

最基本的

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010; 

int f[N][N]; // 背包容积为j时,从前i件物品中可以取得的最大值 
int v[N], w[N]; // v价值, w重量 

int main()
{
    int n,m;
    cin >> n >> m;
    for ( int i = 1; i <= n; i++ ) cin >> w[i] >> v[i]; 
    for ( int i = 1; i <= n; i++ ) 
        for ( int j = 1;  j <= m; j++ ) 
        	f[i][j] = max( f[i-1][j], f[i-1][j-w[i]]+v[i] ); //状态转移 
    cout << f[m] << endl; 
	// f初始全为0,f[m]就是答案, 若只有f[0][0]初始为0,需要遍历最后一行找最大值 
	// 后一种找到的最大值的位置即前一种最大值第一次出现位置,该位置之后均为-INF 
    return 0;
}

优化:一维数组

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1010;

int f[N];
int v[N], w[N];

int main()
{
    int n,m;
    cin >> n >> m;
    for ( int i = 1; i <= n; i++ ) { //大循环每层多放出件物品,其实i是不是从0->n无所谓的,只要遍历了n件物品即可
    	cin >> w >> v; //这里也不需要再开数组了
        for ( int j = m; j >= w[i]; j-- ) { //j从后向前遍历是为了不影响同一层(当前处理同一件物品)中靠前的j对应的f值
            //采用一维数组, 每一轮循环中f[j]相当于继承了上一轮的值,只要判断把当前这件放入j空间的背包能不能取得更高的效益即可
            f[j] = max( f[j], f[j-w]+v );
        }
    }
    cout << f[m] << endl; //价值提高的可能总是先出现在数组右边,且总有f[i]>=f[i-1]
    return 0;
}

完全背包

#include <iostream>
using namespace std;

const int N = 1010;
int f[N];

int main()
{
    int n,m,v,w;
    cin >> n >> m;
    for ( int i = 1; i <= n; i++ ) {
        cin >> w >> v;
        for ( int j = w; j <= m; j++ ) { //从小到大循环,后面添加的物品叠加在前面的里
            f[j] = max( f[j], f[j-w]+v );
        }
    }
    cout << f[m] << endl;
    return 0;
}

多重背包

三循环

#include <iostream>
#include <climits>
using namespace std;

const int N = 1010;
int f[N];

int main()
{
	int n,m;
	int v,w,s;
	cin >> n >> m;
	for ( int i = 1; i <= n; i++ ) {
		cin >> w >> v >> s;
		for ( int j = m; j >= w; j-- ) {
			for ( int k = 1;  k <= s && k*w<=j ; k++ ) {
				f[j] = max( f[j], f[j-k*w]+k*v ); 
			}
		}
	}
	cout << f[m] << endl;
}

二进制优化

把每组物品按规则拆开加入物品栏中,然后按01背包做

#include <iostream> 
#include <vector>
#include <algorithm>

using namespace std;

struct node{
	int v,w;
};

int main()
{
	int n, m;
	cin >> n >> m;
	vector<node> vec;
	int v,s,w;
	for ( int i = 0; i < n; i ++ ) {
		cin >> w >> v>> s;
		for ( int j = 1; j <= s; j *= 2 ) {
			s -= j;
			vec.push_back( {v*j, w*j} );
		}
		if( s ) vec.push_back( {v*s, w*s} );
	}
	int f[m+5] = {0};
	for ( int i = 0; i < vec.size(); i ++ ) {
		for ( int j = m; j >= vec[i].w; j-- ) {
			f[j] = max( f[j], f[j-vec[i].w]+vec[i].v );
		}
	}
	cout << f[m] << endl;
 } 
 

单调队列优化

对每一个物品i,将状态 f[i][j] 按(j%w)划分成w组,对每一组按单调队列做

#include <iostream>

using namespace std;

const int N = 20010;

int dp[N], pre[N], q[N]; // q单调队列 
int n, m;

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) { // 对第i件物品 
        memcpy(pre, dp, sizeof(dp)); // pre是dp的一个副本 
        int v, w, s;
        cin >> v >> w >> s;
        for (int j = 0; j < v; ++j) { // j为该物品的分组 
            int head = 0, tail = -1;  // 队列头尾 , 闭区间 
            for (int k = j; k <= m; k += v) { // 每组中从小到大的容积 

                if (head <= tail && k - s*v > q[head]) // head <= tail 即队列不空 
                    ++head;  // k代表空间,如果把这件物品全放进去了还比队列头大(k>=j+s*v),就把头出队,因为头之后的元素更大且符合限制条件 

                while (head <= tail && pre[q[tail]] - (q[tail] - j)/v * w <= pre[k] - (k - j)/v * w)
                    --tail;

                if (head <= tail)
                    dp[k] = max(dp[k], pre[q[head]] + (k - q[head])/v * w);

                q[++tail] = k;
            }
        }
    }
    cout << dp[m] << endl;
    return 0;
}

举报

相关推荐

0 条评论