[題解]2019-2-28考試:天平問題(背包和母函數)

前言:

第一篇题解,寫點自己在網上搜到的東西,雖然沒什麼卵用......

不會排版還要慢慢學......

作為一個菜雞可能以後發的大多是歌曲推薦(???或者是奇奇怪怪的東西(???


 題目其實在hdu 1709,有多組數據

題面:

小C为了试验小X,便为物竞的小X出了一道物理相关的题:现在给出n个质量的砝码,问小X能称出多少种质量的物品,可是总有好事者想要破坏,于是乎,n达到了500,远远超出了小X能够承受的范围,锲而不舍的他决定寻求你们的帮助。

30%:n <=10
60%: n<=100
100%: n<=500 数据保证砝码的质量之和不超过20000

樣例輸入:                   樣例輸出:

3                                 13

1

3

9

注意:天平有两边,两边均可放。


一、01背包

狀態轉移方程:f[ j ]=f[ j-a[ i ] ]或f[ j+a[ i ] ]

至於減法只要減一下取個絕對值就可以了

 手測了一點點樣例,應該沒問題,要是有錯我再改

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,a[505],sum;
int f[20005];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]),sum+=a[i];
    }
    
    f[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=sum;j++)
        if(f[j+a[i]]) f[j]=1;
        
        for(int j=sum;j>=1;j--)
        if(f[abs(j-a[i])]) f[j]=1;
    }
    
    int ans=0;
    for(int i=1;i<=sum;i++)ans+=f[i];

    printf("%d",ans);
    return 0;
}

2019-03-01 00:24:50

二、普通母函數

 自己在網上看的博客,就是整理一下,寫一些我覺得比較重要的

原文:https://blog.csdn.net/yu121380/article/details/79914529

例題:有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?

我們先在1g和2g間枚舉:(以下為引用)

(使用1g || 不使用1g)&&(使用2g || 不使用2g)

=使用1g&&使用2g || 不使用1g&&使用2g || 使用1g&&不使用2g || 不使用1g&&不使用2g

每个用 “||”分开的一项都是一种方案。“&&”表示选择第一项的情况下有选择了第二项。

大家可以发现这个表达式和一种表达式很像,没错,如果把“||”看成加法,“&&”看成乘法,和多项式的乘法一模一样。那么我们直觉的想到,有没有可能用多项式乘法来表示组合的情况呢?

我们再来看题目,题目需要的是几种砝码组合后的重量,是一个加法关系,但是在上式中“&&”是一种类似于乘法的运算关系,这怎么办呢?

有没有什么这样一种运算关系,以乘法的形式运算,但是结果表现出类似于加法的关系呢?正好有一个,那就是幂运算。Xm 乘上Xn结果是Xm+n,他完美的符合了我们的要求。那么以次数表示砝码的质量, 就可以以多项式的形式表示砝码组合的所有方案。

还是以前两个砝码为例说明,表示1g砝码的两种状态的多项式就是(x0+x1),表示2g的就是(x0+x2),x0表示没有使用砝码,所以重量为0,当然了因为x0 =1,所以也可以写成1,后面为了方便我会这么写。注意,砝码的质量是以次数表示的,而不是直觉上的用下标表示为x1,x2。这点很重要,不然的话就无法表现出幂运算的关系了。

(x0+x1)*(x0+x2)

=x0*x0 + x1*x0 + x0*x2 + x1*x2

=x0 + x1 + x2 + x3

很显然,有四种方案,0g、1g、2g、3g,结果与我们穷举的结果相同,而如果结果中有相同的项,那么合并同类项后每一项的系数就是这种重量有几种实现方案,这会在我们用此方法解决4个砝码问题的时候得到证明。

那么接下来试试用这种表达式表示4个砝码的组合情况

(x0+x1)* (x0+x2) * (x0+x3)* (x0+x4)

=x0 + x1 + x2 + 2x3 + 2x4 + 2x5 + 2x6 + 2x7 + x8+ x9 + x10

解釋的非常清楚了,關於這道題的不同點是左右都能放砝碼,考慮減小的情況

所以這道題转化为(1+x^a[1])*(1+x^a[2])*(1+x^a[3])……(1+x^a[n])

接下來代碼,細節注釋在代碼里

然而代碼仍然是抄的,因為還不太理解,反正先發出來就是了

#include<iostream>
#include<cstdio>
using namespace std;
int n,a[505],sum,ans;//sum为所有砝码的和也就是上界 
int sup[20005],temp[20005];
//sup为當前運算后的結果(被乘多項式),存的是多项式的系数
//下标為多項式的次數,也就是砝码的重量 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];
    for(int i=0;i<=a[1];i+=a[1])sup[i]=1;
    //初始化第一個多項式,也就是用a[1]的多項式
    for(int i=2;i<=n;i++){//生成后续的第i个多项式
        for(int j=0;j<=sum;j++)//遍历当前结果多项式的第j項,與第i個多項式相乘 
        for(int k=0;j+k<=sum && k<=a[i];k+=a[i]){//遍歷第i個多項式的每一項 
            if(k>=j)
            temp[k-j]+=sup[j];
            else temp[j-k]+=sup[j];
            temp[j+k]+=sup[j];
        }
        for(int j=0;j<=sum;j++){//將臨時的結果覆蓋當前結果,同時初始化temp 
            sup[j]=temp[j];
            temp[j]=0;
        }
    }
    for(int i=1;i<=sum;i++)
    if(sup[i]!=0)ans++;//如果這一項係數為0,代表不能拼成 
    printf("%d",ans);
}

寫博客有點耗時間啊,以後還是有把握了再發博客吧,或者不務正業什麼的

2019-03-01 19:57:54

猜你喜欢

转载自www.cnblogs.com/superminivan/p/10454189.html