数字 (number)

数字(number)

为了庆祝祖国的66岁生日, 小Z拿来了一个(2)行$ n $列的方格, 现在他想往里面填入$1,2,…,2n $的数字, 使得每行从左到右递增, 每一列从上到下递增。
小Z随便填了几个数字之后想考一考你, 现在总共有多少种方案呢?

Input

第一行包含一个整数(n)
接下来(2)行, 每行(n)个整数, 若是0则表示未填入, 否则表示已填入这个数字。

Output

输出一行一个数字表示答案,不对任何数取模

Example

输入 #1

(3)
(0) (3) (0)
(2) (0) (0)

输出 #1

(2)

Scoring

对于 10%的数据, 满足 (n≤5)
对于 30%的数据, 满足 (n≤20)
对于 100%的数据, 满足 (n≤1000)

知道是DP但是不会,比赛敲了个暴力还错了

真的废物
————————————————————————————————————————
先来填几个看看

左上角必填1

(1) (-) (-) (...)
(-) (-) (-) (...)

2有两个位置可以填

(1) (2) (-) (...)
(-) (-) (-) (...)
(1) (-) (-) (...)
:-: :-: :-: :-:
(2) (-) (-) (...)

对于上面那种填法,3可以填

(1) (2) (3) (...)
(-) (-) (-) (...)
(1) (2) (-) (...)
:-: :-: :-: :-:
(3) (-) (-) (...)

下面那种则是

(1) (3) (-) (...)
(2) (-) (-) (...)

(...)

可以看出以下两个性质

1.填好的格子都是挨在一起的

2.第一行填的数字个数必不少于第二行(用反证法非常容易,这里就不证了,因为我讲不清楚

这俩性质说明位置((i,j))的种类必来自于((i,j-1))((i-1,j))(如果有这个点)

因此,只要从((1,1))开始推就好了

如果碰上已经填好的,就判断一下这里填的是不是刚好就是我们本来就打算填的数,是的话就给这个点加上我们从前面推过来的数,不是的话就不加

递推式:(f[i][j])表示第一列填了(i)个数,第二列填了(j)个数,(f[i][j])本身指做到当前情况所拥有的种类数

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

另,我们看到题目的输出格式非常玄妙:

输出一行一个数字表示答案,不对任何数取模

不对任何数取模

当这个2行数组全空时,答案为卡特兰数,肥肠大,所以要写个高精

讲完了,看代码。

(虽然我懂了但是代码还是挂,感谢whx大佬的指导,以下代码大量参考他写的正解)

//:D
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
const long long maxn = 1e18;

struct code {//高精
	long long ch[55];
	int len;
	void work (){//进位 
		for (int i = 1; i <= len; i++)
			if (ch[i] >= maxn) ++ch[i + 1], ch[i] -= maxn;
		while (len > 1 && ch[len] == 0)	 --len;
	}
	void add (code x){//加一个数 
		len = max (len, x.len) + 1;
		for (int i = 1; i <= len; i++) ch[i] += x.ch[i];
		work();
	}
	void print(){
		work();
		printf("%lld",ch[len--]);
		for(int i = len; i >= 1; i--)
			printf("%018lld", ch[i]);
	}
} f[N][N];
int n, k[2][N], ok[N<<1];//ok[i]==1表示i已经被填过表格 

int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {scanf("%d", &k[0][i]); ok[k[0][i]] = 1;}
	for (int i = 1; i <= n; i++) {scanf("%d", &k[1][i]); ok[k[1][i]] = 1;}

	f[0][0].ch[1] = 1, f[0][0].len = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= i; j++){
			int p = i + j;//现在在(i,j),填到i+j
			if (i > j && 
			  ((k[0][i] == 0 && ok[p] == 0) || k[0][i] == p))
			//如果上一排填的比下一排多(合法) 并且 
			//这个位置没有被填过数且要填的数没有被用过 或者 这个位置填好的就是这个数 
				f[i][j] = f[i - 1][j];
			if (j && ((k[1][j] == 0 && ok[p] == 0) || k[1][j] == p))
				f[i][j].add(f[i][j - 1]);
		}

	f[n][n].print();
	return 0;
}
原文地址:https://www.cnblogs.com/Sheffield/p/13360818.html