【noip模拟题】华尔街的秘密
Description
最近美国人致力于掀翻华尔街的资本家们,因为他们的生活并不太好,可是那些资本家却依然过着奢华的享乐生活。一些人在游行,而另一些人则试图揭开华尔街的金融家们获取财富的秘密,终于,白宫发现了这个秘密,那就是——一个括号矩阵!!!
所谓括号矩阵,就是一个n*m的矩阵,其中的每一个元素要么是’(‘,要么是’)’。矩阵中有公共边的格子是相邻的。从矩阵的左上角(1,1)到右下角(n,m)的一条路径,如果满足下一步要么走到右边相邻的格子,要么走到下边相邻的格子,它就是一条最短路径。一条路径是合法的,当且仅当它形成的括号序列是可以匹配的,例如(())()、()、(()(()))等等都是可匹配的,而)(、(()、(()))(等等是不可匹配的。一个括号矩阵合法,当且仅当其所有的最短路径合法。
现在,我们定义两个大小相等的合法括号矩阵a和b的比较。c数组是和括号矩阵大小相等的矩阵,规模也是n*m,其中的元素是1~n*m间整数,且互不相同,即n*m个整数出现且仅出现了一次。找到满足如下条件的i,j,a[i][j]≠b[i][j],且c[i][j]最小。若a[i][j]=’(‘,则a < b,否则a > b。
现在奥巴马已经得知某个括号矩阵里面藏着金融家们的致富密码,他会告诉你三个整数n,m,k,你需要找出第k小的n*m的矩阵,这就是那个秘密矩阵。
Input
输入第一行有三个整数,n,m,k。
接下来有n行,每行m个数,第i行第j个数表示c[i][j]。
Output
输出一个n*m的矩阵,由‘(’和‘)’组成。
Sample Input
1 2 1
1 2
Sample Output
()
Hint
n,m<=100, k<=10^18
1<=c[i][j]<=n*m,c中所有元素保证不同
保证存在答案矩阵
Source
刘峻琳
首先感谢刘峻琳学长留下的这道题目。
还有,题目样例太水,这里提供一组加强版(大大的方便了解题)
input
7 10 1
14 29 33 8 1 70 49 62 54 44
46 12 20 2 61 26 56 69 48 31
16 13 3 65 27 59 68 10 40 18
15 4 53 25 47 42 36 17 37 34
5 64 38 55 45 60 30 35 6 22
66 32 67 50 51 9 57 19 21 11
39 52 43 58 23 7 63 24 28 41output
()((()()))
)((()()))(
((()()))((
(()()))(((
()()))((()
)()))((())
()))((()))
题目大概就是这个样子了。
我们读一读题,发现是让我们输出一个以c[][]数组为映射(或者这样理解,就是说按照c数组的大小进行比较)的第
大的合法矩阵。
那么什么是合法的矩阵呢?题目中说道,就是从左上角只能够向右或者向下走到右下角的所有路径都是合法的括号序列。
怎么做呢?
我们得出一个惊人的事实:所有的路径都是同一个括号序列
我们试图从样例来解释一番
()((()()))
)((()()))(
((()()))((
(()()))(((
()()))((()
)()))((())
()))((()))扫描二维码关注公众号,回复: 3296842 查看本文章
是不是竖着的一样呢?
或者说,从右上到左下的对角线都是一样的。
所以我们只需要找出第
大的长度为
括号序列即可。
注意,这里的比较方式不是从左往右,而是按照C数组的顺序。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline static int read() {
char c; int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') rec=rec*10+c-'0',c=getchar();
return rec;
}
int n,m; long long K;
int c[105][105],Rank[10005],vis[205];
int code[205],order[205];
bool overflow[205][205];
//括号序列的总数是超过long long范围的,但是K是在long long范围之内的。
long long f[205][205];
inline void Dp() {
memset(f,0,sizeof(f));
memset(overflow,0,sizeof(overflow));
f[0][0]=1;
for(int i=0;i<n+m-1;i++)
for(int j=0;j<=i;j++){
if(code[i+1]!=-1) {
f[i+1][j+1]+=f[i][j];
if(f[i+1][j+1]<0||overflow[i][j]) overflow[i+1][j+1]=1;
//所以说,只要f[][]的大小超过了long long,那么显然超过了K
}
if(code[i+1]!=1&&j>0) {
f[i+1][j-1]+=f[i][j];
if(f[i+1][j-1]<0||overflow[i][j]) overflow[i+1][j-1]=1;
}
}
return ;
}//在当前已选的部分括号序列的情况下,可能的括号序列的个数
int main() {
n=read(); m=read(); cin>>K;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) {
c[i][j]=read();
Rank[c[i][j]]=i+j-1;
}//Rank是当前位置在括号序列中对应的排名
int tot=0;
for(int i=1;i<=n*m;++i)
if(vis[Rank[i]]==0) {
order[++tot]=Rank[i];
vis[Rank[i]]=1;
if(tot==n+m-1) break;
}//得到比较括号序列大小的顺序
for(int i=1;i<n+m;++i) {
int p=order[i];
code[p]=1;
Dp();
if(f[n+m-1][0]>=K||overflow[n+m-1][0]) continue;
code[p]=-1;
K-=f[n+m-1][0];
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j)
code[i+j-1]==1?putchar('('):putchar(')');
puts("");
}//根据括号序列输出合法矩阵
return 0;
}