• 数字 (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;
    }
    
  • 相关阅读:
    字符串匹配算法 【微软面试100题 第三十三题】
    交换元素,使两数组之和的差最小 【微软面试100题 第三十二题】
    在从1到n的正数中1出现的次数 【微软面试100题 第三十题】
    栈的push、pop序列 【微软面试100题 第二十九题】
    整数的二进制表示中1的个数 【微软面试100题 第二十八题】
    跳台阶问题 【微软面试100题 第二十七题】
    左旋转字符串 【微软面试100题 第二十六题】
    字符串中找出最长的数字串 【微软面试100题 第二十五题】
    合并链表 【微软面试100题 第二十四题】
    计算圆形是否和正方形相交 【微软面试100题 第二十三题】
  • 原文地址:https://www.cnblogs.com/Sheffield/p/13360818.html
Copyright © 2020-2023  润新知