背包类DP

背包问题

01背包问题
有N件物品和一个容量为V的背包。放入第i件物品耗费的费用是ci,得到的价值是wi。求解将哪些物品装入背包可使价值总和最大。

集合求和

题目描述
对于从1到N (1<=N<=39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。
举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:
{3} 和 {1,2}
这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)
如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分法的子集合各数字和是相等的:
{1,6,7} 和 {2,3,4,5} {注 1+6+7=2+3+4+5} {2,5,7} 和 {1,3,4,61,3,4,6} {3,4,73,4,7} 和 {1,2,5,6} {1,2,4,7} 和 {3,5,6}
给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出00。程序不能预存结果直接输出
这个题目也是0/1背包,就是关于第i个数取或不取的问题。
初始状态: d p [ 1 ] [ 0 ] = 1 dp[1][0]=1 dp[1][0]=1; d p [ 1 ] [ 1 ] = 1 dp[1][1]=1 dp[1][1]=1;
状态方程: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − i ] ( j > = i ) dp[i][j]=dp[i−1][j]+dp[i−1][j−i](j>=i) dp[i][j]=dp[i1][j]+dp[i1][ji](j>=i);
第一个部分不取,第二个部分取 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ( i > j ) dp[i][j]=dp[i−1][j](i>j) dp[i][j]=dp[i1][j](i>j);
因为算了两次,所以答案除以2

#include<bits/stdc++.h>
using namespace std;
long long f[9001][9001];
long long n;
int main()
{
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
	cin>>n;
	long long sum=n*(n+1)/2;
	if(sum%2)
	{
		cout<<0<<endl;
		return 0;
	}
	else
	{
		long long v=sum/2;
		f[0][0]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=sum;j++)
				f[i][j]=f[i-1][j]+f[i-1][j-i];
		cout<<f[n][v];
	}
	return 0;
} 

采药

题目描述
宁智贤是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。
医师为了判断他的资质,给他出了一个难题。
医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。
如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是宁智贤,你能完成这个任务吗?
输入格式
输入的第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例数据
input
70 3
71 100
69 1
1 2
output
3
不难写出状态转移方程
f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − v [ i ] ] + w [ i ] ) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]) f[i][j]=max(f[i][j],f[i1][jv[i]]+w[i])
但是会爆空间,所以可以用滚动数组来写
当然可以把二维数组优化到一维数组,这里我们介绍一个模板
我们通过观察二维数组的状态表示,发现 f [ i ] [ c ] f[i][c] f[i][c]只与 f [ i − 1 ] f[i−1] f[i1]这一层有关系,所以我们考虑将i这个维度给优化掉。进一步发现 f [ i ] [ c ] f[i][c] f[i][c]只与 f [ i − 1 ] [ c ] f[i−1][c] f[i1][c] f [ i − 1 ] [ c − w [ i ] ] f[i−1][c−w[i]] f[i1][cw[i]]有关,如果想把i这个维度省掉,第i层的 f [ c ] f[c] f[c]只会与第i−1层的 f [ c ] f[c] f[c] f [ c − w [ i ] f[c−w[i] f[cw[i]有关,因为 f [ c − w [ i ] ] f[c-w[i]] f[cw[i]] f [ c ] f[c] f[c]的左边,所以

for(int i=1;i<=n;i++)
  for(int c=C;c>=0;c--)
    if(c>=w[i]) f[c]=max(f[c],f[c-w[i]+v[i]);

那么这样看来,这个题是不是很()妙 了呢
对了还要注意恰好的时候要让f[0]=0,其它为-∞;

#include<bits/stdc++.h>
using namespace std;
int t,m;
int p[1100],v[1100];
int f[1100];
int main()
{
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
	cin>>t>>m;
	for(int i=1;i<=m;i++) cin>>p[i]>>v[i];
	memset(f,0,sizeof(f));
	f[0]=0;
	for(int i=1;i<=m;i++)
		for(int j=t;j>=1;j--)
			if(j>=p[i])
				f[j]=max(f[j],f[j-p[i]]+v[i]);
	cout<<f[t];
}

最大约数和

题目描述
选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。
输入输出格式
输入格式
输入一个正整数S。
输出格式
输出最大的约数之和。
输入输出样例
输入样例 #1
11
输出样例 #1
9
求因子和就先把所范围里的因子先求出来,作为价值(预处理)
让sum[1]=0 往后推就可以了
状态转移方程
f [ j ] = m a x ( f [ j ] , f [ j − i ] + s u m [ i ] f[j]=max(f[j],f[j-i]+sum[i] f[j]=max(f[j],f[ji]+sum[i];

#include<bits/stdc++.h>
using namespace std;
int sum[1100];
int m;
int f[1100];
void ss()
{
	for(int j=2;j<=1010;j++)
	{
			for(int i=1;i<=sqrt(1.0*j);i++)
			{
				if(i==sqrt(1.0*j))
				{
					sum[j]+=i;
					break;
				}
				if(i==1) sum[j]-=j;
				if(j%i==0)
				{
					sum[j]+=i;
					sum[j]+=j/i;
				}
			
			}
	}
}
int main()
{
	freopen("divsum.in","r",stdin);
	freopen("divsum.out","w",stdout);
	cin>>m;
	ss();
	sum[1]=0;
	for(int i=1;i<=m;i++)
		for(int j=m;j>=0;j--)
			if(j>=i)
				f[j]=max(f[j],f[j-i]+sum[i]);
	cout<<f[m];
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值