组合数学 - 母函数 --- 模板 + 详解

Square Coins

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8341    Accepted Submission(s): 5674

Problem Description

People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland.
There are four combinations of coins to pay ten credits:

ten 1-credit coins,
one 4-credit coin and six 1-credit coins,
two 4-credit coins and two 1-credit coins, and
one 9-credit coin and one 1-credit coin.

Your mission is to count the number of ways to pay a given amount using coins of Silverland.

Input

The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300.

Output

For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output.

Sample Input

2

10

30

0

Sample Output

1

4

27

Source

Asia 1999, Kyoto (Japan)



Mean:

analyse:

母函数的模板题。后面会讲有关母函数的介绍。

Time complexity:O(n^3)

Source code:

// Memory   Time
// 1347K     0MS
// by : Snarl_jsb
// 2014-09-18-10.23
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<climits>
#include<cmath>
#define N 305
#define LL long long
using namespace std;

vector<int> val;
vector<int>c1(N),c2(N);
void make_square()
{
    for(int i=1;i<=18;++i)
        val.push_back(i*i);
}
int main()
{
     ios_base::sync_with_stdio(false);
     cin.tie(0);
//    freopen("C:\\Users\\ASUS\\Desktop\\cin.cpp","r",stdin);
//    freopen("C:\\Users\\ASUS\\Desktop\\cout.cpp","w",stdout);
    make_square();
    int n;
    while(cin>>n,n)
    {
        for(vector<int>::iterator iter=c1.begin();iter!=c1.end();++iter)
            *iter=1;
        c2.clear();
        for(int i=2;i<=17;++i)
        {
            for(int j=0;j<=n;++j)
            {
                for(int k=0;k+j<=n;k+=val[i-1])
                {
                     c2[j+k]+=c1[j];
                }
            }
            for(int j=0;j<=n;++j)
            {
                c1[j]=c2[j];
                c2[j]=0;
            }
        }
        cout<<c1[n]<<endl;
    }
    return 0;
}

  

母函数(Generating Function)

No1.What is the Generating Function?

我只说我的理解,其他的官方定义自行百度。

首先,母函数是一个序列的表达式,由他可以表达一个有规律的序列,我们通过它可以求得这个序列,从而解决一些问题。

母函数,顾名思义,就是母亲,那就说明,在这个函数里面还有儿子,即子函数。说白了,就是子函数可以看作是母函数的一个子集。

而如何把这些子函数用一个母函数来表示呢?即所谓的通项公式,我个人觉的这是问题的症结之处,解决了这一症结,那么,后面的问题就容易多了。

为什么母函数具有这样的功能呢?

从这两句话中你可能有一些启发:

"把组合问题的加法法则和幂级数的t的乘幂的相加对应起来"

"母函数的思想很简单—就是把离散数列和幂级数一一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造. "

No2.What type of problem for Generating Function?

母函数有普通型的,也有指数型的,当然,在ACM中指数型的至今还未碰到,所以这里只讲一下普通型的。

我们知道,母函数在组合数学中运用得非常广泛,主要是用来求解可重集的组合数。

母函数最近经典的运用莫过于整数拆分,即:对于给定的一个数n,问n的拆分有多少种情况?

又如:有n种物品,其中第i种物品有a1个,第二种有a2个,第三种有a3个....而且第1个物品的价值为v1,第二个的为v2....现在问你从中选择若干件物品出来,使得这些物品的价值之和为m,共有多少种选择方式?

这就是母函数的原形,母函数就是求解这类问题的。

No3.How to Structure a Generating Function for a problem?

构造一个序列的母函数,这个要看具体问题,并没有真正通用的模板,但是母函数的构造有很多相同之处。

下面用一个例子来说明:

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

考虑用母函数来接吻这个问题:

我们假设x表示砝码,x的指数表示砝码的重量,这样:

1个1克的砝码可以用函数1+x表示,

1个2克的砝码可以用函数1+x2表示,两个2克的砝码可以用函数1+x4表示,

1个3克的砝码可以用函数1+x3表示,两个三克的可以用函数1+x6表示,...

1个4克的砝码可以用函数1+x4表示,....

我们拿1+x2来说,前面已经说过,x表示砝码,x的指数表示重量,即这里就是一个质量为2的砝码,那么前面的1表示什么?1代表重量为2的砝码数量为0个。

1+x2表示了两种情况:1表示质量为2的砝码取0个的情况,x2表示质量为2的砝码取1个的情况。

这里说下各项系数的意义:

在x前面的系数a表示相应质量的砝码取a个,而1就表示相应砝码取0个,这里可不能简单的认为相应砝码取0个就该是0*x2

几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:

(1+x)(1+x2)(1+x3)(1+x4)

=(1+x+x2+x3)(1+x3+x4+x7)

=1+x+x2+2x3+2x4+2x5+2x6+2x7+x8+x9+x10  

从上面的函数知道:可称出从1克到10克,系数便是方案数

例如右端有2x5 项,即称出5克的方案有2:5=3+2=4+1;同样,6=1+2+3=4+2;10=1+2+3+4。

故称出6克的方案有2,称出10克的方案有1 。

No4.How to solve polynomial expanasion?

可以说整个母函数求解过程中,多项式的展开是一个难点,也是解题的关键。

多项式展开:

我们用两个数组c1[],c2[]来求解,c1[i]=b表示: xi  前面的系数为b,即表示bxi  的系数b;

而c2[]数组只起到中间存值的作用,它是用来维护c1迭代的不变性的。

步骤:

1)根据第一个表达式(第一个括号)给c1赋初值;

2)从第二个表达式开始,依次与c1合并,并将中间结果保存到c2[],然后将c2复制给c1,c2清零。

3)重复第二步,直到所有的表达式都合并完成。

4)统计得出答案(具体看题目要求)。

模板代码:

//
//第1种物品的价值为1,有无限个;
//第2种物品的价值为2,有无限个;
//第3种物品的价值为3,有无限个;
//.......
//问取出一些物品的价值总和为n的取法有多少个?
#include <iostream>
using namespace std;

const int _max = 10001;
int c1[_max], c2[_max];
// c1是保存各项质量砝码可以组合的数目
// c2是中间量,保存每一次的情况,维持c1的不变性
int main()
{
	int nNum;
	int i, j, k;
	while(cin >> nNum)	//要组合的目标数
	{
		for(i=0; i<=nNum; ++i)   // ---- ① 首先对c1初始化,由第一个表达式(1+x+x2+..xn)初始化,把质量从0到n的所有砝码都初始化为1.
		{
			c1[i] = 1;
			c2[i] = 0;
		}
		for(i=2; i<=nNum; ++i)   // ----- ②i从2到n遍历,这里i就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。
		{
			for(j=0; j<=nNum; ++j)   // -----③j 从0到n遍历,这里j就是只一个表达式里第j个变量,比如在第二个表达式里:(1+x2+x4....)里,第j个就是x2*j.
				for(k=0; k+j<=nNum; k+=i)  // ---- ④	k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。
				{
					c2[j+k] += c1[j];
				}
			for(j=0; j<=nNum; ++j)     // ---- ⑤把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的
			{
				c1[j] = c2[j];
				c2[j] = 0;
			}
		}
		cout << c1[nNum] << endl;
	}
	return 0;
}

  

时间: 2024-10-28 21:42:17

组合数学 - 母函数 --- 模板 + 详解的相关文章

Square Coins (HDU 1398) ———母函数模板详解

Square Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7748    Accepted Submission(s): 5238 Problem Description People in Silverland use square coins. Not only they have square shapes but

Django框架模板详解

Django模板详解 模板使用 模板基本由两个部分组成,一是HTML代码,二是逻辑控制代码. 逻辑控制的实现又基本由三个部分组成: 1. 变量的使用 {{ person_name }} #使用双大括号来引用变量 2. tag的使用 {% if ordered_warranty %} #使用大括号和百分号的组成来表示使用Django提供的 template tag {% for item in item_list %} <li>{{ item }}</li> {% endfor %}

c3p0-config.xml模板详解

c3p0-config.xml模板详解 <c3p0-config> <default-config> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.Default: 3 --> <property name="acquireIncrement">3</property> <!--定义在从数据库获取新连接失败后重复尝试的次数.Default: 30 --> <property name=&qu

26.C++- 泛型编程之类模板(详解)

在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板   类模板介绍 和函数模板一样,将泛型思想应用于类. 编译器对类模板处理方式和函数模板相同,都是进行2次编译 类模板通常应用于数据结构方面,使得类的实现不在关注数据元素的具体类型,而只关注需要实现的功能 比如: 数组类,链表类,Queue类,Stack类等 使用方法 通过template关键字来声明,然后通过typename关键字来定义模板类型,如下图所示: 类模板的使用 定义对象时,必须指定类模板类型,因为编译器

25.C++- 泛型编程之函数模板(详解)

1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数模板 当我们想写个Swap()交换函数时,通常这样写: 但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量.更糟的是,还会增加可执行文件的大小. 函数模板 一种特殊的函数,可通过不同类型进行调用 函数模板是C++中重要的代码复用方式 通过template关键字来声明使用模板 通过

Dockerfile创建镜像模板详解

Dockerfile是一个文本格式的配置文件,用户可以使用Dockfile快速创建自定义的镜像.Dockerfile是由一行行的命令语句组成,并且支持以#开头的注释行.vi Dockerfile#一般而言,Dockerfile分为四部分:基础镜像信息.维护者信息.镜像操作指令和容器启动是执行的指令.模板如下: #This dockerfile uses the ubuntu image#指定基于的基础镜像,可以使用多个FROM指令,每个镜像一次#FROM <image>或FROM <im

生成函数(母函数)入门详解

本文章从以上两位大佬的博客参考而来!再次感谢! 母函数,又称生成函数,是ACM竞赛中经常使用的一种解题算法,常用来解决组合方面的题目. 在数学中,某个序列的母函数(Generating function,又称生成函数)是一种形式幂级数,其每一项的系数可以提供 关于这个序列的信息.使用母函数解决问题的方法称为母函数方法. 母函数可分为很多种,包括普通母函数.指数母函数.L级数.贝尔级数和狄利克雷级数.对每个序列都可以写出以 上每个类型的一个母函数.构造母函数的目的一般是为了解决某个特定的问题,因此

【转】 C++模板详解

C++模板 模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数.返回值取得任意类型. 模板是一种对类型进行参数化的工具: 通常有两种形式:函数模板和类模板: 函数模板针对仅参数类型不同的函数: 类模板针对仅数据成员和成员函数类型不同的类. 使用模板的目的就是能够让程序员编写与类型无关的代码.比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型

组合数学 - 母函数 + 模板总结

// Memory Time // 1347K 0MS // by : Snarl_jsb // 2014-09-19-18.23 #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<vector> #include<queue> #include<stack>