动态规划-背包类

(一)01背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量(或说是空间)是v[i], 价值是c[i],每种物品只有一件,求能装的最大价值

二维解法:

定义二维数组f[i][j],表示前i件 不超过j的物品的最大价值

动态转移方程:(根据第i件物品取或不取)

f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + c[i]);

文字的解释:

i件重不超过j的物品的最大价值=取下面两项之最优

1 f[i-1][j]表示前i-1件重j的物品的最大价值,也就是 第i件没取,前i件的最大价值就是前i-1件的最大价值;

2 f[i-1][j-w[i]] + c[i] 表示前i-1件重j-w[i]的物品的最大价值 也就是第i件取了,占了w[i]的重量,增加了c[i]的价值

解是 f[n][V]  (前n个物品重不超过V的最大价值)

 

#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001][1001];  // f[i][j] --> 前i个物品,重量不超过j的最大价值 

int main() {
    cin >> V >> n;
    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
    for(int i=1; i<=n; i++) { //循环前i件物品
        for(int j=V; j>0; j--) { //循环重量j
            if(v[i] <= j) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + c[i]);
            else f[i][j] = f[i-1][j];      //放不下 
        }
    }
    cout << f[n][V] << endl;
    return 0;
}



一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:(根据第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

j循环必须是j=V...0

#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001];  // f[j] --> 重量不超过j的最大价值 

int main() {
    cin >> V >> n;
    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
    for(int i=1; i<=n; i++) {        //循环前i件物品
        for(int j=V; j>=v[i]; j--) { //循环重量j (j<v[i]时放不下,不操作)
            f[j] = max(f[j], f[j-v[i]] + c[i]);   
        }
    }
    cout << f[V] << endl;
    return 0;
}




(二)完全背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

每种物品有无限件,求能装的最大价值



一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:同01背包一维解法一样(根据第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

01不同的是:

j循环必须是j=0...V

重不超过j的物品的最大价值

解是f[V](重量不超过V的最大价值)


#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001];  // f[j] --> 重量不超过j的最大价值

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
	for(int i=1; i<=n; i++) {        //循环前i件物品
		for(int j=v[i]; j<=V; j++) { //循环重量j (j<v[i]时放不下,不操作)
			f[j] = max(f[j], f[j-v[i]] + c[i]);
		}
	}
	cout << f[V] << endl;
	return 0;
}



(三)多重背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

每种物品有s[i]件,求能装的最大价值


一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:(根据第i件物品取或不取)

i=1...n;

   j=V...0;

      k=0...s[i]     //K循环件数s[i]

     f[j] = max(f[j], f[j-w[i]*k] + c[i]*k);

 (多重背包与01背包一样:j循环是j=0...V)

解是f[V];



#include <iostream>
using namespace std;

int n, V;
int v[200], c[200], s[200];
int f[1001];  // f[j] --> 前需要占重量不超过j的最大价值

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
	for(int i=1; i<=n; i++) {
		for(int j=V; j>=0; j--) {
			for(int k=0; k<=s[i]; k++) {
				if(j-v[i]*k < 0) break; //k是递增的,背包不够k循环结束
				f[j] = max(f[j], f[j-v[i]*k] + c[i]*k);
			}
		}
	}
	cout << f[V] << endl;
	return 0;
}




(四)混合背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

有的物品有1件,有的有s[i]件,有的有无限件,求能装的最大价值


解法:

i=1...n;

if(第i件是完全背包) ..

else if(第i件是多重背包) ..

//只能取1件作为s[i]为1的物品

 

可以根据混合情况变通if语句

 

三种背包-混合背包:


#include <iostream>
using namespace std;

int n, V;
int v[1001], s[1001], c[1001];
int f[1001];

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
	for(int i=1; i<=n; i++) {
		if(s[i] == 0) {
			for(int j=v[i]; j<=V; j++)
				f[j] = max(f[j], f[j-v[i]]+c[i]);

		} else {
			for(int j=V; j>=v[i]; j--)
				for(int k=1; k<=s[i]; k++) { 
					if(j >= k * v[i]) {
						f[j] = max(f[j], f[j - k * v[i]] + k * c[i]);
					} else break;
				}
		}
	}
	cout << f[V] << endl;
	return 0;
}



(五)二维费用背包

二维费用只是直接在原有背包上加一维度和循环

 

1原来f[j]表示V不超过j的最大价值

现在f[j][k]表示V1不超过j且V2不超过k的最大价值

 

2 原来f[j] = max(f[j],f[j-v[i]] + c[i]);

现在f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);

 

这样连三维费用背包都会了..

 

这里直接是二维费用的混合三种背包

 



#include <iostream>
using namespace std;

int n, V1, V2;
int v1[1001], v2[1001], m[1001], c[1001];
int f[1001][1001];

int main() {
    cin >> n >> V1 >> V2;
    for(int i=1; i<=n; i++) cin >> v1[i] >> v2[i] >> m[i] >> c[i];
    for(int i=1; i<=n; i++) {
        if(m[i] == 0) { //如果第i件是完全背包 
            for(int j=v1[i]; j<=V1; j++) { 
                for(int k=v2[i]; k<=V2; k++) {
                    f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);
                }
            }
        } else { //如果第i件数量有限(01或多重) 
            for(int j=V1; j>=v1[i]; j--) {
                for(int k=V2; k>=v2[i]; k--) {
                    for(int s=1; s<=m[i]; s++) {
                        if(j - s*v1[i] < 0 || k - s*v2[i] < 0) break;
                        f[j][k] = max(f[j][k], f[j-v1[i]*s][k-v2[i]*s] + c[i]*s);
                    }
                }
            }
        }
    }
    cout << f[V1][V2] << endl;
    return 0;
}


(六)有依赖性的背包

 

【金明的预算方案】

 

【解法】分五种情况处理主件:不取,只取主件,取主件和附件1,取主件和附件2,取主件和两个附件.找最优情况

 

#include <iostream>
using namespace std;

int V, n, num;     //V是金钱,n是物品总数,num记录主件数 
int v[61][4];      //v[i][0]记录有几个附件了,v[i][1]:主件的价格,v[i][2]:附件1,v[i][3]:附件2 
int p[61][4];      //同上,记录重要度 
int f[61][32001];  //f[i][j]表示前i件主件价格为j的最大价值 
int map[61];       //i映射到v、p数组(num) 

int main() {
	int tmpv, tmpp, follow;
	cin >> V >> n;
	for(int i=1; i<=n; i++) {
		cin >> tmpv >> tmpp >> follow;
		if(follow == 0) {
			v[++num][1] = tmpv;
			p[num][1] = tmpp;
			map[i] = num;
			continue;
		}
		follow = map[follow];
		v[follow][++v[follow][0]+1] = tmpv;
		p[follow][++p[follow][0]+1] = tmpp;
	}
	for(int i=1; i<=num; i++) {
		for(int j=1; j<=V; j++) {
			f[i][j] = f[i-1][j];
			if(j >= v[i][1]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]]+v[i][1]*p[i][1]);
			if(j >= v[i][1]+v[i][2]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]);
			if(j >= v[i][1]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][3]]+v[i][1]*p[i][1]+v[i][3]*p[i][3]);
			if(j >= v[i][1]+v[i][2]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]-v[i][3]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]+v[i][3]*p[i][3]);
		}
	}
	cout << f[num][V] << endl;
	return 0;
}

 

(七)分组背包

N件物品,第i个物品的重量是v[i], 价值是c[i],每个物品有自己的组,每组只能选一个物品,求能装的最大价值

(可以看下原题  洛谷-分组背包

#include <iostream>
using namespace std;

int v[200][1010], c[200][1010];
int F[1010];
int V, N, G;

int main() {
	int v1, c1, opt;
	cin >> V >> N;
	for(int i=1; i<=N; i++) {
		cin >> v1 >> c1 >> opt;
		G = max(G, opt);
		v[opt][++v[opt][0]] = v1;
		c[opt][++c[opt][0]] = c1;
	}
	for(int i=1; i<=G; i++)
		for(int j=V; j>=0; j--) 
			for(int k=1; k<=v[G][0]; k++)
				if(j >= v[i][k])
					F[j] = max(F[j], F[j-v[i][k]] + c[i][k]);
	cout << F[V] << endl;
	return 0;
}



QWQ,就这样了...#end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值