壬寅朔月的背包和指针(上)

1.背包

1.1 01背包

概览
       1998年的石溪布鲁克大学算法库的研究表明,在75个算法问题中,背包问题是第18个最受欢迎,第4个最需要解决的问题(前三为后kd树,后缀树和bin包装问题)。

       背包问题出现在各种领域的现实世界的决策过程中,例如寻找最少浪费的方式来削减原材料,选择投资和投资组合,选择资产支持资产证券化,和生成密钥为Merkle-Hellman和其他背包密码系统。

       背包算法的一个早期应用是在测试的构建和评分中,测试者可以选择他们回答哪些问题。对于小例子来说,这是一个相当简单的过程,为测试者提供这样的选择。例如,如果考试包含12个问题,每个问题的价值为10分,测试者只需回答10个问题即可获得100分的最高分。然而,在点值的异质分布的测试 - 即不同的问题值得不同的点值 - 更难以提供选择。 Feuerman和Weiss提出了一个系统,其中学生被给予一个异质测试,共有125个可能的点。学生被要求尽可能回答所有的问题。在总点数加起来为100的问题的可能子集中,背包算法将确定哪个子集给每个学生最高的可能得分。
 

思路

1.状态转移方程

假设他的剩余容量为t,已经放入的当前总价值为dp[t],下一个要放入的物品代价是c[i],价值是v[i]

分两种情况:①->维持原状态不变 ②->吸纳这个物品,

所以得到状态转移方程

dp[t] = max(dp[t],dp[t - c[i]] + v[i]);

2.循环次序

外层-物品数

内层:只需对剩余容量位于(c[i],t]进行遍历

重要:对于01背包,一个物品只有一次机会,所以要倒着遍历,从t到c[i]

3.实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
int v[10001],c[10001]; 
int dp[10005];
signed main(){
	int t,m;
	scanf("%lld%lld",&t,&m);
	for(int i = 1;i <= m;i++){
		scanf("%lld%lld",&c[i],&v[i]);
	}
	for(int i = 1;i <= m;i++){
		for(int j = t;j >= c[i];j--){
			dp[j] = max(dp[j - c[i]] + v[i],dp[j]);
		}
	}
	printf("%lld",dp[t]);
	return 0;
}

1.2 完全背包 :在01背包的基础上,将物品个数改为无限个

状态方程不变

对于完全背包,一个物品有不限机会,所以要正着遍历,从t=c[i]到t

#include<bits/stdc++.h>
#define int long long
using namespace std;
int v[10001],c[10001]; 
int dp[10005];
signed main(){
	int t,m;
	scanf("%lld%lld",&t,&m);
	for(int i = 1;i <= m;i++){
		scanf("%lld%lld",&c[i],&v[i]);
	}
	for(int i = 1;i <= m;i++){
		for(int j = c[i];j <= t;j++){
			dp[j] = max(dp[j - c[i]] + v[i],dp[j]);
		}
	}
	printf("%lld",dp[t]);
	return 0;
}

3.多重背包:介于1,2之间

思路:进行合成 转化为01背包

合成方法:最好是2进制优化(O(N)->O(logN))

//此处未完待续

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10, M = 2e6 + 100;

int n,m,T,ans = 0,sum = 0;

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

void solve(){
    cin >> n >> m;
    int cnt = 1;	
    for(int i = 1 ;i <= n ; i ++){
        int a,b,c;	//体积,重量,数量
        cin >> a >> b >> c;
        int k = 1;//(2的0次等于1)
        //将53---->转换成22的过程
        while(k<=c){
            v[cnt] = a * k;
            w[cnt++] = b * k;
            c-=k;
            k*=2;//2的k次
        }
        //22也放入01中
        if(c>0){
            v[cnt] = a * c;
            w[cnt++] = b * c;
        }
    }
    for(int i = 1 ; i <= cnt ; i ++){
        for(int j = m ; j >= v[i] ; j --){
            f[j] = max(f[j],f[j-v[i]] + w[i]);
        }
    }
    cout << f[m] << endl;
}

signed main(){
        solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值