Uva 10497 - Sweet Child Makes Trouble 解题报告(递推+大数)

96 篇文章 0 订阅

Problem F
Sweet Child Makes Trouble
Input: standard input
Output: standard output
Time Limit: 8 seconds

Children are always sweet but they can sometimes make you feel bitter. In this problem, you will see how Tintin, a five year’s old boy, creates trouble for his parents. Tintin is a joyful boy and is always busy in doing something. But what he does is not always pleasant for his parents. He likes most to play with household things like his father’s wristwatch or his mother’s comb. After his playing he places it in some other place. Tintin is very intelligent and a boy with a very sharp memory. To make things worse for his parents, he never returns the things he has taken for playing to their original places.

Think about a morning when Tintin has managed to ‘steal’ three household objects. Now, in how many ways he can place those things such that nothing is placed in their original place. Tintin does not like to give his parents that much trouble. So, he does not leave anything in a completely new place; he merely permutes the objects.

Input

There will be several test cases. Each will have a positive integer less than or equal to 800 indicating the number of things Tintin has taken for playing. Each integer will be in a line by itself. The input is terminated by a –1 (minus one) in a single line, which should not be processed.

Output

For each test case print an integer indicating in how many ways Tintin can rearrange the things he has taken.

Sample Input

2
3
4
-1

Sample Output

1
2
9


    解题报告: 推起来并不难。当有三个物品,而且都不在自己位置上,那么情况数应该是:3的全排列 - 一个物品在自己位置上 - 两个物品在自己位置上。
    用 f(n) 表示n个物品都不在自己位置上的情况数。那么 f(n) = n! - C(n, 1) * f(n-1) - C(n, 2) * f(n-2) - ... - 1(都在自己位置上)。
    公式不难推,但800不好弄。n! 的结果近2000位。计算C可以递推,但是要开800*800的数组,如果和n!计算,势必要开成相同的2000位。此时一定会超内存的。
    笔者没有那么好的按需分配内存的大数模版,所以用Java搞定了。代码如下:
package main;

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
	static private Scanner cin = new Scanner(System.in);
	static private BigInteger[] factorial = new BigInteger[888];
	static private BigInteger[][] C = new BigInteger[888][888];
	static private BigInteger[] result = new BigInteger[888];
	
	public static void init()
	{
		factorial[0]=new BigInteger("1");
		for(int i=1;i<=800;i++)
			factorial[i] = factorial[i-1].multiply(new BigInteger(""+i+""));
		
		for(int i=0;i<=800;i++)
			for(int j=0;j<=800;j++)
				C[i][j] = new BigInteger("0");
		
		for(int i=0;i<=800;i++)
		{
			C[i][0]=new BigInteger("1");
			for(int j=1;j<=i;j++)
			{
				C[i][j] = C[i-1][j-1].add(C[i-1][j]);
			}
		}
		
		result[0] = new BigInteger("0");
		result[1] = new BigInteger("0");
		result[2] = new BigInteger("1");
		
		for(int i=3;i<=800;i++)
		{
			result[i] = factorial[i].subtract(new BigInteger("1"));
			for(int j=1;j<i-1;j++)
			{
				result[i] = result[i].subtract(C[i][j].multiply(result[i-j]));
			}
		}
	}
	
	public static void main(String[] args) {
		init();

		int n;
		n = cin.nextInt();
		while(n>-1)
		{
			System.out.println(result[n]);
			n = cin.nextInt();
		}
	}
}
    不知神牛又是怎么做的……
    和队友讨论了一下,队友找到了一份C++版的解题报告……思想基本如下:
    当前需要放置N个元素,都不在自己位置。我们可以从N-1个元素都不在自己位置的情况里加一个元素X。从N-1中选一元素Y,那么此时Y一定不在它本来的位置上,交换Y和X,产生所需要的序列,情况数为 (n-1)*f(n-1)。上一种情况中X一定不在Y本来的位置上,第二种情况就是将X放在Y本来的位置上,Y放在X的位置上,其他元素的情况数则为f(n-2),那么总情况数为 (n-1)*(f(n-1)*f(n-2)),套个大数模板,分分钟搞定……代码如下:
/*
 * 大数模版
 * 完美支持负数及加减法操作
 * 支持乘法,大小比较,赋值
 * 支持与long long类型的赋值比较
 * SF-_- 14.04.01
 */
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int K = 10000;    // 数组里每位代表1W
const int M = 300;       // 一共10位
const char show[] = "%04lld";

struct Bignum
{
    LL a[M*2];          // 大数数组
    int len;            // 长度
    bool negative;      // 正负

    Bignum()
    {
        clear();
    }

    void clear()
    {
        len=0;
        negative=false;
        memset(a, 0, sizeof(a));
    }

    Bignum(LL num)
    {
        *this=num;
    }

    Bignum operator=(LL num)
    {
        clear();
        if(num<0) negative=true, num=-num;
        while(num)
            a[len++]=num%K,num/=K;
        return *this;
    }

    Bignum(const Bignum& cmp)
    {
        memcpy(this, &cmp, sizeof(Bignum));
    }

    Bignum operator=(const Bignum& cmp)
    {
        memcpy(this, &cmp, sizeof(Bignum));
        return *this;
    }

    int absCmp(const Bignum& cmp)
    {
        if(len!=cmp.len)
            return len>cmp.len?1:-1;

        for(int i=len-1;i>=0;i--)
            if(a[i]!=cmp.a[i])
                return a[i]>cmp.a[i]?1:-1;

        return 0;
    }

    int absCmp(LL num)
    {
        Bignum cmp(num);
        return absCmp(cmp);
    }

    bool operator<(const Bignum& cmp)
    {
        if(negative^cmp.negative)
            return negative?true:false;

        if(negative)
            return absCmp(cmp)>0;
        else
            return absCmp(cmp)<0;
    }

    bool operator<(LL num)
    {
        Bignum cmp(num);
        return *this<cmp;
    }

    bool operator==(const Bignum& cmp)
    {
        if(negative^cmp.negative)
            return false;
        return absCmp(cmp)==0;
    }

    bool operator==(LL num)
    {
        Bignum cmp(num);
        return *this==cmp;
    }

    void absAdd(const Bignum& one, const Bignum& two)
    {
        len=max(one.len, two.len);
        for(int i=0;i<len;i++)
        {
            a[i]+=one.a[i]+two.a[i];
            if(a[i]>=K) a[i]-=K, a[i+1]++;
        }
        if(a[len]) len++;
    }

    void absSub(const Bignum& one, const Bignum& two)
    {
        len=one.len;
        for(int i=0;i<len;i++)
        {
            a[i]+=one.a[i]-two.a[i];
            if(a[i]<0) a[i+1]--,a[i]+=K;
        }
        while(len>0 && a[len-1]==0) len--;
    }

    void absMul(const Bignum& one, const Bignum& two)
    {
        len=one.len+two.len;
        for(int i=0;i<one.len;i++) for(int j=0;j<two.len;j++)
            a[i+j]+=one.a[i]*two.a[j];
        for(int i=0;i<len;i++) if(a[i]>=K)
            a[i+1]+=a[i]/K,a[i]%=K;
        while(len>0 && a[len-1]==0) len--;
    }

    Bignum operator+(const Bignum& cmp)
    {
        Bignum c;
        if(negative^cmp.negative)
        {
            bool res = absCmp(cmp)>0;
            c.negative = !(negative^res);
            if(res)
                c.absSub(*this, cmp);
            else
                c.absSub(cmp, *this);
        }
        else if(negative)
        {
            c.negative=true;
            c.absAdd(*this, cmp);
        }
        else
        {
            c.absAdd(*this, cmp);
        }
        return c;
    }

    Bignum operator-(const Bignum& cmp)
    {
        Bignum cpy;
        if(cpy==cmp)
            return *this;
        else
            cpy=cmp, cpy.negative^=true;

        return *this+cpy;
    }

    Bignum operator*(const Bignum& cmp)
    {
        Bignum c;
        if(c==cmp || c==*this)
            return c;

        c.negative = negative^cmp.negative;
        c.absMul(*this, cmp);
        return c;
    }

    void output()
    {
        if(len==0)
        {
            puts("0");
            return;
        }

        if(negative)
            printf("-");

        printf("%lld", a[len-1]);
        for(int i=len-2;i>=0;i--)
            printf(show, a[i]);
        puts("");
    }
};

Bignum res[801];

void init()
{
    res[1]=0;
    res[2]=1;
    for(int i=3;i<=800;i++)
        res[i] = (res[i-1]+res[i-2])*(i-1);
}

int main()
{
    init();

    int n;
    while(~scanf("%d", &n) && n>=0)
        res[n].output();
}
    好吧,思维不及大牛也,惭愧,惭愧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值