【算法每日一练]-背包类型 篇2 摆花 ,自然数拆分

今天还是讲一下背包问题,昨天讲的有点多,今天上几道题

目录

题目:摆花

  思路:

题目:自然数拆分

  思路:


        

        

题目:摆花

        

          

思路:

我们设置dp[i][j]表示当前遍历到了第i种花且一共放了j盆,那么最终求dp[n][m]即可,

映射方程:dp[i][j]=dp[i-1][j-1]+dp[i-1][j-2]+……+dp[i-1][j-k](k为i种花的盆数)

好了,转成一维:f[j]+=f[j-k]    (k从0开始取)

        

如果你想用多重背包的话也可以,要注意这里求的是摆法,不是最大价值。

转移方程由f[j]=max(f[j],f[j-k*v]+k*w),变化为f[j]+=f[j-k]即可。

        

 初始化问题:[0][0]应该为1,咱们要的是种类,自己推一下应该是这样的。

//  二维背包写法
#include <bits/stdc++.h>       
using namespace std;
const int maxn=105,mod=1000007;
int n,m,a[maxn],f[maxn][maxn];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	f[0][0]=1;
	for(int i=1;i<=n;i++){//上板子,外层是物品
		for(int j=0;j<=m;j++){//内层是容量
			for(int k=0;k<=min(j,a[i]);k++)//因为物品不只一个,故每种都要试一下
			f[i][j]=(f[i][j]+f[i-1][j-k])%mod;     //二维动态规划
		}
	}
	cout<<f[n][m]<<'\n';
}

或者

#include <bits/stdc++.h>    //正常写法   
using namespace std;
const int maxn=105,mod=1000007;
int n,m,a[maxn],f[maxn];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	f[0]=1;
	for(int i=1;i<=n;i++)
		for(int j=m;j>0;j--)   
			for(int k=1;k<=min(j,a[i]);k++)//或for(int k=1; k<=j&&k<=a[i]; k++)
				f[j]=(f[j]+f[j-k])%mod;   
	cout<<f[m]<<'\n';
	return 0;
}



当然还有一种写法:记忆化dfs,后面会讲到,别急 

//先见上一面
int dfs(int x,int k)    //递推dfs写法 (+记忆化)
{
    if(k>m) return 0;//无效
    if(k==m) return 1;
    if(x==n+1) return 0;//无效
    if(f[x][k]) return f[x][k]; //搜过了就返回
    int ans=0;
    for(int i=0; i<=a[x]; i++) ans=(ans+dfs(x+1,k+i))%mod;//f[i][j]由f[i+1][j+……]得来
    f[x][k]=ans; //记录当前状态的结果
    return ans;
}

   


    

题目:自然数拆分

   

 思路:

裸的完全背包,因为参加加法运算的可以重复。

注意题目要求对答案模2147483648。这里数字较大,要开 long long

    

#include <bits/stdc++.h> 
using namespace std; //完全背包
long long MOD=2147483648; 
int n;
long long f[4010]; //注意f可能会很多,如果开int就过不了
int main()
{
    scanf("%d",&n);
    f[0]=1; //初值化
    for(int i=1;i<n;i++) //物品最多只能到n-1
    {
        for(int j=i;j<=n;j++) //背包容量至少要从i开始
            f[j]=(f[j]+f[j-i])%MOD; 
    }
    printf("%d",f[n]);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值