题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/vans)
描述
郊区呈矩形,有四条东西方向的街道和N(1<=N<=1000)条南北方向的街道。在郊区的西北角有一个邮局。
如N=5时,郊区如下图所示,圆点表示邮局,直线表示街道。
每天邮政卡车从邮局出发,每个十字路口(包括边界和四角)经过且只经过一次。现在邮局希望知道邮政货车行驶的路线有几种。 例如,下面两幅图表示的是满足上图的两条路线
另一个例子,下面四幅图表示了当N=3时的全部四种情况
[编辑]格式
PROGRAM NAME: vans
INPUT FORMAT:
(file vans.in)
INPUT FORMAT 一行:一个数值N
OUTPUT FORMAT:
(file vans.out) 一行: 到INPUT中给出的街道的路径总数
[编辑]SAMPLE INPUT
4
[编辑]SAMPLE OUTPUT
12
解题思路:
- 参考“北极天南星”的解题报告
- 需要高精度计算,用数组来代表一个大数。根据进位借位原理实现大数的加法和减法,乘法直接用多次加法来实现(基本都是乘2乘3,乘较大数的乘法只用过2次,所以用加法实现足以)
- 最后特殊的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;
}