Time Limit: 1 second

Memory Limit: 128 MB

【问题描述】

有这样一种数列A1、A2、A3、……An,其中A1=0,且对任意一项Ai满足|Ai-A(i+1)|=1(1<=i<n)。设S=A1+A2+A3+……+An,表示前n项之和。 现在给出数列长度n与数列前n项之和S,要求: 输出满足条件的数列的总数。 

输出满足条件的100个数列(如果不满100个就全部输出)。

【输入格式】

一行,包含两个整数n和S(1<=n<=100),用1个空格隔开。

【输出格式】

第1行一个整数t(0<=t<=263-1),表示满足条件的数列总数。 接下来每行输出一个数列,数列各项之间用一个空格隔开。 若满足条件的数列数目不满100个,全部输出即可。

Sample Input

4 0

Sample Output

2
0 -1 0 1
0 1 0 -1

【题解】

设f[i][j]为前n个数,组成和为s的方案数。

f[i+1][j+i] =f[i+1][j+i]+ f[i][j];

f[i+1][j-i] =f[i+1][j-i]+ f[i][j];

更新的时候这样更新

for (int i = 1;i <= n;i++)

for (int j = -9999;j <= 10000;j++)

if (f[i][j] > 0)

{

f[i+1][j+i] =f[i+1][j+i]+ f[i][j];

f[i+1][j-i] =f[i+1][j-i]+ f[i][j];

}

//先忽略数组下标是负的这个问题。

首先是边界问题。

f[1][0] = 1;{0}

f[2][1] = 1;{0,1}

f[2][-1] = 1;{0,-1}

比如我们现在枚举到

i = 2;j = 1;

j+i== 3;

f[3][3] = f[3][3] + f[2][1];

为什么?

f[2][1]->{0,1};

这里我们进行的操作相当于在0和1之间插入一个数字1.

然后原来的1递增。

即{0,1,2} 也即f[3][3];

然后j-i==-1;

f[3][-1] =f[3][-1]+f[2][1];

为什么?

f[2][1]->{0,1};

这里我们进行的操作相当于在0和1之间插入一个数字-1;

然后原来的1要递减才能满足|a[n]-a[n+1]|==1;

即{0,-1,0}也即f[3][-1];

再往下举例子;

i = 4,j==6;

{0,1,2,3};

对应状态f[4][6];

然后j+i==10;

则f[5][10] = f[5][10]+f[4][6];

为什么?

f[4][6]->{0,1,2,3};

这里我们相当于在0和1之间插入一个1.然后把1,2,3都递增1;

也即{0,1,2,3,4};也即f[5][10];

然后j-i ==2;

则f[5][2] = f[5][2]+f[4][6];

为什么?

f[4][6]->{0,1,2,3}

这里我们相当于在0和1之间插入一个-1.然后原来的其他数字全都递减1.

即{0,-1,0,1,2};也即f[5][2];

加深理解:

每次进行枚举序列的时候必然要对第i号元素做出决策。

决策的内容就是当前数字比前一个数字大1还是小1;

假设我们每个阶段做出的决策为what[i] ∈{-1,1};

则题目的要求就是

(0+what[1]) + (0+what[1]+what[2]) + (0+what[1]+what[2]+what[3])+..+

(0+what[1]+what[2]+..+what[n-1]) == s

可以合并同类项为

what[n-1]+2*what[n-2]+3*what[n-3]+..+(n-1)*what[1] == s;

然后what[i]只能为1或-1

则原问题转化为

0口1口2口3口4口..口(n-1) == s

其中的口要填入减号或加号。

这个动规就比较好写了

f[i][j] = f[i-1][j-(i-1)]+f[i-1][j+(i-1)];

//等号右边分别表示最后一个口做加法和做减法的情况;

这也是我们上面那种解释的写法。只不过变成顺推了而已。

f[i+1][j+i] += f[i][j]

我们插入1和插入-1到a[1]和a[2]之间。

原本是

0+(0+what[1]) + (0+what[1]+what[2]) + (0+what[1]+what[2]+what[3])+..+

(0+what[1]+what[2]+..+what[i-1]) == j

我们插入一个1之后就变成这样了

0+(0+1)+(0+what[1]+1)+(0+what[1]+what[2]+1)+(0+what[1]+what[2]+what[3]+1)

+..+(0+what[1]+what[2]+..+what[i-1]+1) == j + i

//what下标从1..i-1,然后a[2]变成(0+1)还有一个1.所以总共是i个1.

而这是一个符合要求的序列!

插入-1同理。只不过右边变成j-i了。

然后要输出100个方案的时候。就可以按照上面说的原理。即在第1和第2个数字之间插入1个1或者-1。来获取方案。(用f[i][j]数组来剪枝!)

总和的话1+..+100 为5050.然后考虑到下标会出现i+j的情况。你直接开到10000就好。

数组下标为负数的话 就直接加上20000;

即数组开为f[101][20001];

【代码】

#include <cstdio>

int n, s,what[101],sum;
__int64 f[101][20001] = { 0 };//如果下标为负数就直接加上20000;
__int64 tot; void solve(int); int main()
{
scanf("%d%d", &n, &s);//输入n和s
f[1][0] = 1;//第一个数字为0
for (int i = 1;i <= n-1;i++)//枚举前i个数字
for (int tj = -9999; tj <= 10000; tj++)//枚举总和。
{
int j = tj;
if (j < 0) //小于0就直接加上20000
j += 20000;
if (f[i][j] <= 0) //如果不能到达这个状态就跳过。
continue;
int temp = tj + i ;//这是在第1和第2个数字之间插入一个1的情况,原来的其他数字就都要加上1
if (temp < 0) //同理,小于0就加20000;
temp += 20000;
f[i + 1][temp] += f[i][j];//递增答案。
temp = tj - i;//这是在第1和第2个数字之间插入一个-1的情况,原来的其他数字就都要减去1了。
if (temp < 0)
temp += 20000;
f[i + 1][temp] += f[i][j];//递增相应的答案即可。
}
if (s < 0) //为下标小于0特判
printf("%I64d\n", f[n][s + 20000]);
else
printf("%I64d\n", f[n][s]);
sum = s;//这是当前的和。我们采用递减的方式让sum一直减到0且到达了第一个数字。
if (s < 0) //这是所需要输出的方案数。
tot = f[n][s + 20000];
else
tot = f[n][s];
if (tot > 100)//大于100就直接为100;
tot = 100;
solve(n);//输出方案。
return 0;
} void solve(int now)
{
if (now == 1 && sum == 0)//如果已经到了第一个元素(即0),且sum也从s减到了0.则我们
{//搜索的这个方案是可行的。
tot--;//找到了这个方案。方案数递减。
printf("0");//第一个数字肯定是0
int kk = 0; //第一个数字是0
for (int i = n; i >= 2; i--)//我们是把后面出现的数字插入到a[1]和a[2]之间。
{//所以进行的操作是从后往前的。
kk += what[i]; //对初始数字(一开始kk==a[1]==0)进行递增或递减操作。
printf(" %d", kk);//输出kk。
}
printf("\n");
return;
}
if (tot == 0)//如果要求的方案数都输出了。则退出
return;
int tt = sum;
if (tt < 0)
tt += 20000;
if (f[now][tt] == 0)//如果前now个数和为tt的情况不存在则退出。
return;//这是一个很棒的剪枝。
what[now] = -1;//表示当前的状态是插入一个-1得来的。
sum += (now - 1);//然后它之前的状态当然就是把-1去掉。然后和要相应的递增。
solve(now - 1);//然后往前继续枚举,要注意一定要先枚举-1的情况(可以看样例输出。这是顺序问题);
sum -= (now - 1);//回溯 what[now] = 1;//表示当前这个状态是插入一个1得来的。
sum -= (now-1);//则它之前的状态就是把这个1去掉。其他数字相应也要减去1(now-1个1);
solve(now - 1);//往前继续枚举。
sum += (now - 1);//回溯。
}

【u004】数列的更多相关文章

  1. C#求斐波那契数列第30项的值(递归和非递归)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  2. BZOJ1500[NOI2005]维修数列

    Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一 ...

  3. PAT 1049. 数列的片段和(20)

    给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段.例如,给定数列{0.1, 0.2, 0.3, 0.4},我们有(0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1 ...

  4. 斐波拉契数列加强版——时间复杂度O(1),空间复杂度O(1)

    对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - ) + F(n - ),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围 ...

  5. fibonacci数列(五种)

    自己没动脑子,大部分内容转自:http://www.jb51.net/article/37286.htm 斐波拉契数列,看起来好像谁都会写,不过它写的方式却有好多种,不管用不用的上,先留下来再说. 1 ...

  6. js中的斐波那契数列法

    //斐波那契数列:1,2,3,5,8,13…… //从第3个起的第n个等于前两个之和 //解法1: var n1 = 1,n2 = 2; for(var i=3;i<101;i++){ var ...

  7. 洛谷 P1182 数列分段Section II Label:贪心

    题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 4 5 1要分成3段 将其如下分段: [4 ...

  8. 剑指Offer面试题:8.斐波那契数列

    一.题目:斐波那契数列 题目:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项.斐波那契数列的定义如下: 二.效率很低的解法 很多C/C++/C#/Java语言教科书在讲述递归函数的时 ...

  9. 代码的坏味道(4)——过长参数列(Long Parameter List)

    坏味道--过长参数列(Long Parameter List) 特征 一个函数有超过3.4个入参. 问题原因 过长参数列可能是将多个算法并到一个函数中时发生的.函数中的入参可以用来控制最终选用哪个算法 ...

随机推荐

  1. 【Educational Codeforces Round 35 B】Two Cakes

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 从小到大枚举x. 看看a/x+b/x是不是大于等于n 是的话. 看看是不是两种蛋糕都能凑一堆. 也即x的最大枚举量是min(a,b) ...

  2. 文字记录而已!!人民币直充/兑换PayPal美金

    人民币直充/兑换PayPal美金 用PAYPAL国际使用外国货币,没有信用卡是不能冲值的,到淘宝买吧,被骗的几率大一些,弄来弄去,PAYPAL被冻结. 今天朋友介绍使用 中美互动网 让它给自己的PAY ...

  3. Web 组件是什么

    Web 组件是什么 一.总结 这篇下面的内容多看 1.组件化的目的:高内聚,低耦合,便于多人同时开发 2.各种前端框架(前端组件化)让写前端不要太简单: 3.组件编程最最最最简单实例: <lin ...

  4. (错误记录)git push 报错 403

    在push的时候遇到错误: RPC failed; HTTP curl The requested URL returned error: Forbidden 如果是自己创建的项目的话,可以在网上找到 ...

  5. 【Codeforces Round #450 (Div. 2) C】Remove Extra One

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 枚举删除第i个数字. 想想删掉这个数字后会有什么影响? 首先,如果a[i]如果是a[1..i]中最大的数字 那么record会减少1 ...

  6. 洛谷 P1170 兔八哥与猎人

    P1170 兔八哥与猎人 题目描述 兔八哥躲藏在树林旁边的果园里.果园有M × N棵树,组成一个M行N列的矩阵,水平或垂直相邻的两棵树的距离为1.兔八哥在一棵果树下. 猎人背着猎枪走进了果园,他爬上一 ...

  7. leetcode 113. Path Sum II (路径和) 解题思路和方法

    Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given su ...

  8. [D3] Animate with the General Update Pattern in D3 v4

    In D3, the General Update Pattern is the name given to what happens when a data join is followed by ...

  9. 编程一一C语言问题,指针函数与函数指针

    资料来源于网上: 一.指针函数:指返回值是指针的函数      类型标识符    *函数名(参数表)       int *f(x,y); 首先它是一个函数,只不过这个函数的返回值是一个地址值.函数返 ...

  10. CentOS 6 通过DVD快速建立本地YUM源

    一.将DVD光盘放入RedHat/CentOS6.X服务器/电脑光驱中 二.挂载DVD光驱到/mnt/cdrom # mkdir -p /mnt/cdrom # mount -t iso9660 /d ...