华尔街的秘密

题目描述

最近美国人致力于掀翻华尔街的资本家们,因为他们的生活并不太好,可是那些资本家却依然过着奢华的享乐生活。一些人在游行,而另一些人则试图揭开华尔街的金融家们获取财富的秘密,终于,白宫发现了这个秘密,那就是——一个括号矩阵!!!

所谓括号矩阵,就是一个 n × m n \times m n×m 的矩阵,其中的每一个元素要么是 (,要么是 )。矩阵中有公共边的格子是相邻的。从矩阵的左上角 ( 1 , 1 ) (1, 1) (1,1) 到右下角 ( n , m ) (n, m) (n,m) 的一条路径,如果满足下一步要么走到右边相邻的格子,要么走到下边相邻的格子,它就是一条最短路径。一条路径是合法的,当且仅当它形成的括号序列是可以匹配的,例如 (())()()(()(())) 等等都是可匹配的,而 )((()(()))( 等等是不可匹配的。一个括号矩阵合法,当且仅当其所有的最短路径合法。

现在,我们定义两个大小相等的合法括号矩阵 a a a b b b 的比较。 c c c 数组是和括号矩阵大小相等的矩阵,规模也是 n × m n \times m n×m,其中的元素是 1 ∼ n × m 1 \sim n \times m 1n×m 间整数,且互不相同,即 n × m n \times m n×m 个整数出现且仅出现了一次。找到满足如下条件的 i , j i, j i,j a i , j ≠ b i , j a_{i, j} \neq b_{i, j} ai,j=bi,j,且 c i , j c_{i, j} ci,j 最小。若 a i , j = ′ ( ′ a_{i, j} = '(' ai,j=(, 则 a < b a < b a<b,否则 a > b a > b a>b

现在奥巴马已经得知某个括号矩阵里面藏着金融家们的致富密码,他会告诉你三个整数 n , m , k n, m, k n,m,k,你需要找出第 k k k 小的 n × m n \times m n×m 的矩阵,这就是那个秘密矩阵。

输入格式

输入第一行有三个整数 n , m , k n,m,k n,m,k

接下来有 n n n 行,每行 m m m 个数,第 i i i 行第 j j j 个数表示 c i , j c_{i, j} ci,j

输出格式

输出一个 n × m n \times m n×m 的矩阵,由 ( ( ( ) ) )组成。

样例

样例输入1:

1 2 1
1 2

样例输出 1:

()

数据范围

n , m ≤ 1000 , k ≤ 1 0 18 n, m \le 1000, k \le 10^{18} n,m1000,k1018

1 ≤ c i , j ≤ n × m 1 \le c_{i, j} \le n \times m 1ci,jn×m c c c 中所有元素保证不同。

保证存在答案矩阵。

题解

题目要求每一个最短路径都是合法的,因此,每个左下到右上的对角线是相同的(因为从某个位置向下向右走到相同的格子,对角线相等)。这样把一个矩阵映射成一个一维序列。

找第 k k k 小的序列, k k k 很大,考虑计数dp。设 f i , j f_{i, j} fi,j 表示到第 i i i 个位置 j j j 个左括号未匹配。 f i , j = f i − 1 , j − 1 + f i − 1 , j + 1 f_{i, j} = f_{i - 1, j - 1} + f_{i - 1, j + 1} fi,j=fi1,j1+fi1,j+1

按照 c c c 的大小依次确定每一位是 ( 还是 ),具体做法就是把这个位置设为 (,看方案数是否超过 k k k,没有超过就改为 )

#include<bits/stdc++.h>
using namespace std;
long long n, m, K;
struct node{
    long long x, y;
}a[210];
bool cmp(node p, node q){
    return p.x < q.x;
}
long long f[210][210], fl[210];
int main(){
    scanf("%lld %lld %lld", &n, &m, &K);
    for(int i = 1; i <= 200; ++ i){
    	a[i].x = 1e16;
	}
    for(long long i = 1; i <= n; ++ i){
        for(long long j = 1; j <= m; ++ j){
            long long x;
            scanf("%lld", &x);
            a[i + j - 1].x = min(a[i + j - 1].x, x);//映射
        }
    }
    n = n + m - 1;
    for(long long i = 1; i <= n; ++ i){
        a[i].y = i;
    }
    sort(a + 1, a + n + 1, cmp);//按c排序
    for(long long i = 1; i <= n; ++ i){
    	//dp
        f[0][0] = 1;
        fl[a[i].y] = 1;
        for(long long j = 1; j <= n; ++ j){
            for(long long k = 0; k <= j; ++ k){
                if(fl[j] == 1){//已经确定
                    f[j][k] = f[j - 1][k - 1];
                }
                else if(fl[j] == 2){//已经确定
                    f[j][k] = f[j - 1][k + 1];
                }
                else{
                    f[j][k] = f[j - 1][k - 1] + f[j - 1][k + 1];
                }
                f[j][k] = min(f[j][k], K);
            }
        }
        if(f[n][0] >= K){
            fl[a[i].y] = 1;
        }
        else{
            fl[a[i].y] = 2;
            K -= f[n][0]; 
        }
    }
    n = n - m + 1;
    for(long long i = 1; i <= n; ++ i){
        for(long long j = 1; j <= m; ++ j){
            if(fl[i + j - 1] == 1){
                putchar('(');
            }
            else{
                putchar(')');
            }
        }
        putchar('\n');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值