寒假笔记·动态规划·背包

01背包

以0与1来表达是否可以达到特定值,从而找到在一定条件下所可以取得的最大值。

例题:P1734 最大约数和

原题地址
题目描述

选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。

输入输出格式

输入格式:
输入一个正整数S。

输出格式:
输出最大的约数之和。

输入输出样例

输入样例#1:
11
输出样例#1:
9
说明

样例说明

取数字4和6,可以得到最大值(1+2)+(1+2+3)=9。

数据规模

S<=1000
代码:
基本的01背包思想,注意找出与01背包的联系(只不过多了一步寻找约数和的过程)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;

int main()
{
    int s,i,j;
    int vv[1010],ss[1010];
    memset(vv,0,sizeof(vv));
    scanf("%d",&s);
    for(i=1;i<=s;i++)
    {
        for(j=1;j<=i/2;j++)
        {
            if(i%j==0) vv[i]=vv[i]+j;
        }
    }
    ss[0]=0;
    for(i=1;i<=s;i++)
        for(j=i;j<=s;j++)
        {
            ss[j]=max(ss[j],ss[j-i]+vv[i]);
        }
    printf("%d\n",ss[s]);
    return 0;
}

隐蔽的背包

有时候并不能一下子看出背包模型

例题:P2563 [AHOI2001]质数和分解

原题地址
题目描述

任何大于 1 的自然数 n 都可以写成若干个大于等于 2 且小于等于 n 的质数之和表达式(包括只有一个数构成的和表达式的情况),并且可能有不止一种质数和的形式。例如,9 的质数和表达式就有四种本质不同的形式:

9 = 2 + 5 + 2 = 2 + 3 + 2 + 2 = 3 + 3 + 3 = 2 + 7 。

这里所谓两个本质相同的表达式是指可以通过交换其中一个表达式中参加和运算的各个数的位置而直接得到另一个表达式。

试编程求解自然数 n 可以写成多少种本质不同的质数和表达式。

输入输出格式

输入格式:
文件中的每一行存放一个自然数 n(2 < n < 200) 。

输出格式:
依次输出每一个自然数 n 的本质不同的质数和表达式的数目。

输入输出样例

输入样例#1:
2
200
输出样例#1:
1
9845164

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n;
int qq[220],dd[200];
int su(int x)
{
    int i;
    for(i=2;i<=sqrt((double)x);i++)
        if(x%i==0) return 0;
    return 1;
}
int main()
{
    int i,j,t=0;
    memset(qq,0,sizeof(qq));
    for(i=2;i<=200;i++)
        if(su(i)) dd[++t]=i;
    qq[0]=1;
    for(i=1;i<=t;i++)
        for(j=dd[i];j<=200;j++)
            if(qq[j-dd[i]]) qq[j]+=qq[j-dd[i]];
    while(cin>>n)
    {
        printf("%d\n",qq[n]);
    }
    return 0;
}

完全背包

设一个数组表示时间,用状态转移方程表示每一体积所能达到的最大价值。

例题:P2722 总分

原题地址
题目描述

我们可以从几个种类中选取竞赛的题目,这里的一个"种类"是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数。你的任务是写一个程序来告诉USACO的职员,应该从每一个种类中选取多少题目,使得解决题目的总耗时在竞赛规定的时间里并且总分最大。输入包括竞赛的时间,M(1 <= M <= 10,000)(不要担心,你要到了训练营中才会有长时间的比赛)和N,“种类"的数目1 <= N <= 10,000。后面的每一行将包括两个整数来描述一个"种类”:

第一个整数说明解决这种题目能得的分数(1 <= points <= 10000),第二整数说明解决这种题目所需的时间(1 <= minutes <= 10000)。

你的程序应该确定我们应该从每个"种类"中选多少道题目使得能在竞赛的时间中得到最大的分数。

来自任意的"种类"的题目数目可能是任何非负数(0或更多)。

计算可能得到的最大分数。

输入输出格式

输入格式:
第 1 行: M, N–竞赛的时间和题目"种类"的数目。

第 2-N+1 行: 两个整数:每个"种类"题目的分数和耗时。

输出格式:
单独的一行包括那个在给定的限制里可能得到的最大的分数。

输入输出样例

输入样例#1:
300 4
100 60
250 120
120 100
35 20
输出样例#1:
605
代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
    int T,n,i,j;
    int p[10005],a[10005][2];
    memset(p,0,sizeof(p));//初始化
    scanf("%d%d",&T,&n);
    for(i=0;i<n;i++)
        scanf("%d%d",&a[i][0],&a[i][1]);
    for(i=0;i<n;i++)
    {
    for(j=a[i][1];j<=T;j++)
        p[j]=max(p[j],p[j-a[i][1]]+a[i][0]);//状态转移方程
    }
    printf("%d\n",p[T]);
    return 0;
}

二维背包

类似完全背包,只不过将状态转移方程从一维转化到了二维。

例题:P1910 L国的战斗之间谍

原题地址
题目描述

俗话说的好:“知己知彼,百战不殆”。L国的指挥官想派出间谍前往I国,于是,选人工作就落到了你身上。

你现在有N个人选,每个人都有这样一些数据:A(能得到多少资料)、B(伪装能力有多差)、C(要多少工资)。已知敌人的探查间谍能力为M(即去的所有人B的和要小于等于M)和手头有X元钱,请问能拿到多少资料?

输入输出格式

输入格式:
N M X

A1 B1 C1

A2 B2 C2

………………

AN BN CN

输出格式:
能得到的资料总数

输入输出样例

输入样例#1:
3 10 12
10 1 11
1 9 1
7 10 12
输出样例#1:
11
说明

数据范围:

1≤n≤100,1≤m≤1000, 1≤x≤1000
代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
int main()
{
    int a,b,c,i,ans,k,n,m,x,j,ss[1010][1010];
    scanf("%d%d%d",&n,&m,&x);
    memset(ss,0,sizeof(ss));
    for(i=0;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        for(j=x;j>=c;j--)
            for(k=m;k>=b;k--)
            {
                ss[j][k]=max(ss[j][k],ss[j-c][k-b]+a);//二维状态转移方程
            }
    }
    printf("%d\n",ss[x][m]);
    return 0;
}

分组背包

例题:P1757 通天之分组背包

原题地址
题目描述

自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。

输入输出格式

输入格式:
两个数m,n,表示一共有n件物品,总重量为m

接下来n行,每行3个数ai,bi,ci,表示物品的重量,利用价值,所属组数

输出格式:
一个数,最大的利用价值

输入输出样例

输入样例#1:
45 3
10 10 1
10 5 1
50 400 2
输出样例#1:
10
说明

1<=m<=1000 1<=n<=1000 组数t<=100
代码:
大致类似于01背包,注意控制循环变量的时候把组数放在外面

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int M=1010,N=1010;
int n,m;
int f[M],a[N],b[N],c[101][20],cc[101],cn;
int main()
{
    scanf("%d%d",&m,&n);
    memset(f,0,sizeof(f));//初始化
    for(int i=1;i<=n;i++)
    {
        int C;
        scanf("%d%d%d",&a[i],&b[i],&C);
        cn=max(cn,C);
        cc[C]++;
        c[C][cc[C]]=i;
    }
    for(int i=1;i<=cn;i++)
        for(int j=m;j>=0;j--)//控制循环变量的时候把组数放在外面
            for(int k=1;k<=cc[i];k++)
            if(j>=a[c[i][k]])
            f[j]=max(f[j],f[j-a[c[i][k]]]+b[c[i][k]]);
    printf("%d\n",f[m]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43918350/article/details/87947545