[Acwing蓝桥杯DP] 1226. 包子凑数

题目链接:1226. 包子凑数 - AcWing题库

题目大意:有n层不同的包子,这n层包子的各不形同,每层的数量是无限的 要用这些层包子凑出不同数量的包子数 如果不能凑出的包子数是无数个,就输出INF,如果不是无数个,就输出有限的不能凑出的包子的个数。

数据范围:1<=n<=100

          要凑出的总包子数是无数个

分析:每层的包子是可以无限使用的

          这就联想到了完全背包问题,每个背包可以无限的用

          关键是总包子数是无限的 这个条件限制了M是无穷大的

          总数到底是多少呢?

这里就要用到数论的知识:

裴蜀定理:任意两个数的组合必定是他们gcd的任意两个数的组合必定是他们gcd的倍数,同样可以推广到更多数:如果这些数的gcd是d,那么他们的组合是d的倍数,如果dd不是1,那么必然有无限个数无法被组合出来。

那么最大的不能表达的数一定要有一个上界M啊

要不状态转移方程怎么定义范围?

当gcd=1,两个数a,b,最大不能表示出来的 数是:(a−1)(b−1)−1当数字更多的时候,这个上界必然更小(可选的数字变多了),而99和98是100内最大的互质的数,所以这个上界选择10000。(暂且无法证明)

那么就用闫氏DP分析法:

集合f [ i ] [ j ] 表示:在前i个数中选任意多个,是否能到达 总数量为 j  ? ( true : false )

属性:是否存在

区域划分

由最后不同的一步来划分集合,在重量为j的时候,第i 个物品选了几件。

可以分为:dp(i,j)=dp(i−1,j) || dp(i−1,j−w(i)) |||| dp(i−1,j−k∗w(i))  (k=j/w(i))

但是,完全背包问题有个特殊的地方:那就是 状态重叠

 

 

 

所以最终方程为:f [ i , j ]=f [  i−1, j ]  ||  f [ i , j−w(i) ]  (w(i)≤j)

初始化:f [ 0 ][ 0 ] = 1; //什么也不选的时候重量为0

那么代码如下:

二维空间的写法(朴素写法)

#include <bits/stdc++.h>

using namespace std;

const int N=110,M=10010;

bool f[N][M];//f[i][j]表示:考虑前i个物品总重量为j的方案是否存在
int n;
int w[N];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

int main()
{
    cin>>n;
    int d=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        d=gcd(d,w[i]);
    }
    if(d!=1)puts("INF");
    else 
    {
        f[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<M;j++)
            {
                f[i][j]=f[i-1][j];
                if(j>=w[i])f[i][j]|=f[i][j-w[i]];
            }
        }
        int res=0;
        for(int i=0;i<M;i++)
        {
            if(!f[n][i])res++;
        }
        cout<<res<<endl;
    }
    return 0;
}
View Code

这样的话空间比较大 毕竟f [ 110] [10010]

优化一下空间:

优化完空间后的写法

#include <bits/stdc++.h>

using namespace std;

const int N=110,M=10010;

bool f[M];//f[i][j]表示:考虑前i个物品总重量为j的方案是否存在
int n;
int w[N];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

int main()
{
    cin>>n;
    int d=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        d=gcd(d,w[i]);
    }
    if(d!=1)puts("INF");
    else 
    {
        f[0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=w[i];j<M;j++)
            {
               f[j]|=f[j-w[i]];
            }
        }
        int res=0;
        for(int i=0;i<M;i++)
        {
            if(!f[i])res++;
        }
        cout<<res<<endl;
    }
    return 0;
}
View Code

END!!!

 

 

 

    

 

posted @ 2022-04-01 22:45  秦末  阅读(75)  评论(0)    收藏  举报
1 博文导航目录