母 函 数 及 背 包 学 习 小 结

最近按宇神的blog学了学背包,写了几道题,现在做一下总结。。。。。

(学的较浅,如有不对,还请指正 Q A Q)

首先是背包的分类:A . 0 1 背包 ,完全背包,多重背包;

先学了01背包和完全背包

分别来解释一下两种背包的含义:

{

    01背包:有一个总容量为M的背包要放进N个物品,物品重量为d,物品的价值为V,每个物品只能放一次,问放哪些物品可以使放的物品总价值最大;

    完全背包:有一个容量为M的背包,有N种物品,每种物品有无限个,问怎么放可以使背包总价值最大;

}

    考虑01背包:每个物品只能有两种选择--->放或者不放,(这也是01背包名称的由来,0代表不放,1代表放)

    那么第 i 个物品的选择不同就会导致背包状态的不同:用F[N,M]函数来表示背包的总价值,放第i个物品时背包的价值

   F[i,m]=max{F[i-1,m],F[i-1,m-d[i]]+v[i]},这个状态转移方程的意义为轮到放入第i个物品时的价值由不放这个物品或者放了这   个   物品的价值这两者的价值最大者决定;所以说第i个物品的状态由上一个物品决定。

于是我们定义一个数组dp,用dp[j]来表示背包物品总重量为j的价值,从M到的d[i]遍历一下,就可以得到结果;

POINT:1.dp[j]=max(dp[j],dp[j-d[i]]+v[i]) (状态转移方程);

            2.由于每个物品只能拿一次,所以为了不重复拿物品,第二层循环要倒序;

(解释POINT2)例子:

最初假设初始化dp为0,然后第一个物品wi = 2,vi = 3;正序遍历的时候,第一次:dp[2] = 3,之后dp[4] = max(dp[4], dp[4 - 2] + 3) = 6;然而这个物品只有一件,所以dp[2]=dp[4]=3;

下面是01背包的模板:

#include<cstdio>
const int maxn=100;
int dp[maxn];
int d[maxn],v[maxn];
int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
		scanf("%d %d",&d[i],&v[i]);
	for(int i=0;i<n;i++)
		for(int j=m;j>=d[i];j--)//倒序是保证i物品的状态是由i-1推出,不会受到i-2,i-3...物品的影响 
			 dp[j]=max(dp[j],dp[j-d[i]]+v[i]);
	return 0;
}

完全背包:由定义我们可以知道,每个物品可以拿多次,所以我们直接正序遍历就行了;其他和01背包一样;

状态转移方程也是一样;

下面是完全背包的模板:

#include<cstdio>
const int maxn=100;
int dp[maxn];
int d[maxn],v[maxn];
int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
		scanf("%d %d",&d[i],&v[i]);
	for(int i=0;i<n;i++)
		for(int j=a[i];j<=m;j++) 
			 dp[j]=max(dp[j],dp[j-d[i]]+v[i]);
	return 0;
}

关于dp数组初始化问题;

一般关于背包的问题有两种问法:恰好装满背包,没有要求恰好装满,第一种问法的话,那么除了dp[0]=0以外,其他均为负无穷,这是因为初始时候只有背包为0有合法解0,其他没有;第二种的话都初始化为0,因为开始没有放进一个物品,所以都为0;

母函数:假如有三种硬币,面值分别为1,2,5,问组成价值为K的硬币有多少种方法;

每种硬币的数目为num1,num2,num3;

G(x)=(1+x+x^2+x^3+...x^num1)(1+x^2+x^4+...x^(2*num2))(1+x^5+x^10+...x^(10*num3));

那么运算完后,设x^n前面的系数为an,那么组成价值为n的钱数的组合数为an;

我们将x的幂当做价值,这样看的话,第一个多项式就是(x^0+x^1+x^2+....x^num1),意思为拿一个一元硬币,拿两个一元硬币,拿num1个硬币;剩下两个多项式同理,那么我们将其相乘,幂相加,再合并同类项,就能得到结果;

展开一下上面的多项式:G(x)=1+a1*x+a2*x^2+......an*x^n;

我们把G(x)称为序列a1,a2,a3....an的母函数;

这种求硬币的母函数为普通型母函数;

下面是普通型母函数的模板:

#include<cstdio>
#include<cstring>
const int maxn=100;
int c1[maxn],c2[maxn];
int cent[3]={1,2,5};
int M;//最大可能得到价值,M=num1+2*num2+5*num3; 
int main()
{
	cin>>M;
	for(int i=0;i<=M;i++)
		c1[i]=1;
	memset(c2,0,sizeof(c2));
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<=M;j++)
			for(int k=0;k+j<=M;k+=cent[i])
				c2[k+j]+=c1[j];//中间数组记录一下 
		for(int j=0;j<=M;j++)
		{
			c1[j]=c2[j];
			c2[j]=0;
		}
	}
	for(int j=0;j<=M;j++)
		printf("%d%c",c1[j],j==M?'\n':' ');
	return 0;
}

还有一种母函数:指数型母函数;

有这样一个问题:有n个数,从这n个数选m个数,问有多少种不同的排列方法;

我们这样考虑,相当于选数,每个数可能出现mk次,由于问的是不同的排列方法,所以要除以阶乘;

比如1 1 2 5 和1 1 2 5 的排列方式是一样的,所以要去除重复的;

那么我们就可以写出他的多项式了:

G(x)=(1+x+x^2/(2!)+x^3/(3!)+....x^m1/(m1!))(1+x+x^2/(2!)+x^3/(3!)+....x^m2/(m2!))....(1+x+x^2/(2!)+x^3/(3!)+....x^mk/(mk!));

下面是其模板:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=11;
LL f[maxn];
double c1[maxn],c2[maxn];
void fa()
{
	f[0]=1;
	for(int i=1;i<maxn;i++)
		f[i]=i*f[i-1];
}
int main()
{
	fa();
	int n,m;
	cin>>n>>m;
	memset(c1,0,sizeof(c1));
	memset(c2,0,sizeof(c2));
	c1[0]=1;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
			for(int k=0;k<num&&k+j<m;k++)
				c2[k+j]+=c1[j]/f[k];
		for(int i=0;i<m;i++)
		{
			c1[i]=c2[i];
			c2[i]=0;
		}
	}
	for(int i=0;i<m;i++)
		printf("%.01f%c",c1[i]*f[i],'\n':' ');	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值