Postal Vans

题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/vans

描述

郊区呈矩形,有四条东西方向的街道和N(1<=N<=1000)条南北方向的街道。在郊区的西北角有一个邮局。

如N=5时,郊区如下图所示,圆点表示邮局,直线表示街道。

postal1.gif

每天邮政卡车从邮局出发,每个十字路口(包括边界和四角)经过且只经过一次。现在邮局希望知道邮政货车行驶的路线有几种。 例如,下面两幅图表示的是满足上图的两条路线

postal2.gif

另一个例子,下面四幅图表示了当N=3时的全部四种情况

postal3.gif

[编辑]格式

PROGRAM NAME: vans

INPUT FORMAT:

(file vans.in)

INPUT FORMAT 一行:一个数值N

OUTPUT FORMAT:

(file vans.out) 一行: 到INPUT中给出的街道的路径总数

[编辑]SAMPLE INPUT

 4

[编辑]SAMPLE OUTPUT

 12

解题思路:

  1. 参考“北极天南星”的解题报告
  2. 需要高精度计算,用数组来代表一个大数。根据进位借位原理实现大数的加法和减法,乘法直接用多次加法来实现(基本都是乘2乘3,乘较大数的乘法只用过2次,所以用加法实现足以)
  3. 最后特殊的dp[2]的算法:dp[i]=dp[i+2]+dp[i+1]-dp[i+3]+sum[i+2]×2(公式转自北极天南星解题报告中),根据这个式子求出dp[2]之后,还需要额外加上sum[3]

代码

/*
ID: zc.rene1
LANG: C
PROG: vans
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_N 1000
#define BITS 512

int N;
int f[MAX_N + 1][BITS];
int sum[MAX_N + 1][BITS];

void Initial(FILE *fin)
{
    fscanf(fin, "%d", &N);

    memset(f, 0, sizeof(f));
    memset(sum, 0, sizeof(sum));

    f[N][0] = 1;
    sum[N][0] = 1;
}

int GetRect(int i, int j)
{
    int ret = 2 * (j - i);

    if (i + 1 == j)
    {
	ret++;
    }
    if (j + 1 == N)
    {
	ret++;
    }
    if (i == 2)
    {
	ret++;
    }

    return ret;
}

void Add(int *a, int *b, int *c)
{
    //a + b = c
    int remain = 0;
    int k, temp;

    for (k=0; k<BITS; k++)
    {
	temp = a[k] + b[k] + remain;
	c[k] = temp % 10;
	remain = temp / 10;
    }
}

void Minus(int *a, int *b, int *c)
{
    //a - b = c
    int remain = 0;
    int k, temp;

    for (k=0; k<BITS; k++)
    {
	temp = a[k] - b[k] - remain;
	if (temp < 0)
	{
	    temp += 10;
	    remain = 1;
	}
	else
	{
	    remain = 0;
	}
	c[k] = temp;
    }
}

void Multiply(int *a, int times, int *b)
{
    //a * times = b
    int j;
    int temp[BITS];

    memcpy(temp, a, BITS * sizeof(int));
    memset(b, 0, BITS * sizeof(int));

    for (j=1; j<=times; j++)
    {
	Add(b, temp, b);
    }
}

void PrintSeq(int *seq, FILE *fout)
{
    int i, flag = 0;

    for (i=BITS-1; i>=0; i--)
    {
	if (seq[i] != 0)
	{
	    flag = 1;
	}
	if (flag == 1)
	{
	    fprintf(fout, "%d", seq[i]);
	}
    }

    if (flag == 0)
    {
	fprintf(fout, "0\n");
    }
    else
    {
	fprintf(fout, "\n");
    }
}
	
void DP(FILE *fout)
{
    int i, j;
    int temp[BITS];

    //f[N - 1] && f[N - 2]
    for (i=N-1; i>=N-2 && i>=2; i--)
    {
	for (j=i+1; j<=N; j++) 
	{
	    Multiply(f[j], GetRect(i, j - 1), temp);
	    Add(f[i], temp, f[i]);
	}
	Add(sum[i + 1], f[i], sum[i]);
    }

    for (i=N-3; i>=3; i--)
    {
	Multiply(sum[i + 2], 2, temp);
	Minus(temp, f[i + 3], temp);
	Add(temp, f[i + 1], temp);
	Add(temp, f[i + 2], f[i]);
	Add(sum[i + 1], f[i], sum[i]);
    }

    if (N - 2 > 2)
    {
	i = 2;
	Multiply(sum[i + 2], 2, temp);
	Minus(temp, f[i + 3], temp);
	Add(temp, f[i + 1], temp);
	Add(temp, f[i + 2], f[i]);

	//f[2] is special, f[2] must add sum[3] to create the right answer
	Add(f[i], sum[i + 1], f[i]);
    }
    
    Multiply(f[2], 2, f[2]);
    PrintSeq(f[2], fout);
}

int main(void)
{
    FILE *fin, *fout;

    fin = fopen("vans.in", "r");
    fout = fopen("vans.out", "w");

    Initial(fin);
    DP(fout);

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值