动态规划——背包专题

1.01背包
放和不放,每种物品只能用一次

#include<iostream>
using namespace std;
#define N 1005
int dp[N]; 
int main()
{
	int n,m,v,w;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
	    cin>>v>>w;
		for(int j=m;j>=v;j--)
		{
			dp[j]=max(dp[j],dp[j-v]+w);
		} 
	}
	cout<<dp[m]<<endl;
	return 0;
}

[01扩展背包——板子]

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,x;
int a[1000];
int b[1000];
int c[1000];
int dp[2000][2000];
int main(){
cin>>n>>m>>x;
for(int i=1;i<=n;i++)
    cin>>a[i]>>b[i]>>c[i];

for(int i=1;i<=n;i++)
{
    for(int j=m;j>=b[i];j--)
    {
        for(int k=x;k>=c[i];k--)
        {
            dp[j][k]=max(dp[j][k],dp[j-b[i]][k-c[i]]+a[i]);
        }
    }
}
cout<<dp[m][x];
}


(https://www.luogu.com.cn/problem/P1910#submit)
2.完全背包
每种物品可无限次使用

从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为F[i] = max(F[i],dp[F-c[i]]+v[i])。

#include<iostream>
using namespace std;
#define N 1005
int dp[N]; 
int main()
{
	int n,m,w,v;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>v>>w;
		for(int j=v;j<=m;j++)
		{
			dp[j]=max(dp[j],dp[j-v]+w);
		} 
	}
	cout<<dp[m]<<endl;
	return 0;
}

3.多重背包例题
多重背包是01背包的扩展,与完全背包不同的是考虑每种物品的数量(不再是无限)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
int dp[100];//dp[i]:取i个物品的最大价值
int v[100];//体积
int w[100];//价值
int num[100];//数量
int m,n;
int main(){

cin>>n>>m;//物品种数和背包zl
for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i]>>num[i];
for(int i=1;i<=n;i++)
  {
      for(int j=m;j>=v[i];j--)
      {
          for(int k=1;k<=num[i];k++)
          {
              if(j>=k*v[i])
              {
                  dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
              }
          }
      }
  }
  cout<<dp[m];
}

4.分组背包P1757 通天之分组背包
不再告诉你每种物品具体有几种,而是告诉你该物品属于那个组

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
int m,n;
int a[2000];
int b[2000];
int c[2000];
int dp[2000];
int main(){

int zu=-1;
cin>>m>>n;
for(int i=1;i<=n;i++)
{
    cin>>a[i]>>b[i]>>c[i];
    zu=max(zu,c[i]);
}
for(int i=1;i<=zu;i++)//穷举组
{
    for(int j=m;j>=0;j--)
    {
        for(int k=1;k<=n;k++)//穷举每件物品
        {
            if(c[k]!=i||j<a[k]) continue;
            dp[j]=max(dp[j],dp[j-a[k]]+b[k]);

        }
    }
}
cout<<dp[m];
}

5.背包求方案数
小a点菜

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int a[105];
int dp[10005];
int main(){

cin>>n>>m;
for(int i=1;i<=n;i++)
    cin>>a[i];
dp[0]=1;
for(int i=1;i<=n;i++)
{
    for(int j=m;j>=a[i];j--)
    {
        dp[j]+=dp[j-a[i]];
    }
}
cout<<dp[m];
}`

P2563 [AHOI2001]质数和分解

#include <bits/stdc++.h>//万能头文件
using namespace std;
int prime[201], c = 1, f[201], n;
bool p[201];
int main(){
    for ( int i = 2; i <= 200; i++ ){
        if ( !p[i] ){
            prime[c++] = i;
            for ( int j = i*2; j <= 400; j += i )
            p[j] = true;
        }
    }//筛法求素数。主要原则:默认所有数为 0 (false) ,如果碰到一个数是 0 ,就把它 N 以内的所有倍数标记成 1 ,并将这个数存进 prime 数组。(#废话
    f[0] = 1;//初值憋忘了。
    for ( int i = 1; i < c; i++ )
    for ( int j = prime[i]; j <= 200; j++ )
    f[j] += f[j-prime[i]];//状态转移2333
    while ( cin >> n )//每当输入
    printf ( "%d\n", f[n] );//输出对应的答案
    return 0;//华丽落幕
}

6.布尔型01背包处理均分(找一半)问题
P1489 猫狗大战

#include<bits/stdc++.h>
using namespace std;
int n;

int sum;
int a[205];
bool dp[2500][2500];//dp[i][j]:i个人的血量和j


int main(){
cin>>n;
for(int i=1;i<=n;i++)
  {
       cin>>a[i];sum+=a[i];
  }
 dp[0][0]=true;
 for(int i=1;i<=n;i++)
 {
     for(int j=n/2;j>=0;j--)//找一半的人数
     {
         for(int k=sum/2;k>=0;k--)//类似于01背包的最大承重
         {
             if(dp[j][k]==true)
             {
                 dp[j+1][k+a[i]]=true;
             }
         }
     }
 }
 for(int i=sum/2;i>=0;i--)//从大到小——》找最大值
 {
     if(dp[n/2][i]==true)
     {
         cout<<i<<" "<<sum-i;
         return 0;
     }
 }
}

这个问题的模板是ZOJ1149——Dividing

dp[0]=1;
for(int i=1;i<=n;i++)
{
  for(int j=sum/2;j>=b[i];j--)
  {
   if(dp[j-b[i]])
   {
      dp[j]=1;
   }
  }
}
if(dp[sum/2]==1) yes
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉梦昂志️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值