Description
要求给一个 n×m 的棋盘上放上左括号或者右括号,使得其满足两个条件,首先,任意一条从 (1,1) 到 (n,m) 的路径(只能往下或往右走)都是一个合法的括号序列,其次,每个位置有一个权值(各不相同),按权值从小到大把位置里的括号拿出来的序列是所有满足第一个条件的方案中字典序第 k 小的,保证有解,输出该序列
Input
第一行三个整数
(1≤n,m≤100,1≤k≤1018,1≤pi,j≤nm)
Output
输出一个 n×m 的括号矩阵
Sample Input
1 2 1
1 2
Sample Output
()
Solution
考虑从 (1,1) 到 (n,m) 的两条路径 (1,1)→(1,m)→(n,m) 和 (1,1)→(1,m−1)→(2,m−1)→(2,m)→(n,m)
这两条路径均为合法括号序列,且前半段和后半段完全一样,区别是第一条路径经过 (1,m) 而第二条路径经过 (2,m−1) ,故这两个位置的括号应该相同,类似的可以证明,行列和固定的位置括号都应该相同,也就是说我们只需要考虑 n+m−1 个位置的括号
由于在考虑字典序时不是按顺序来排列括号而是按权值来排列,故首先要得到这
n+m−1
个位置的权值,对于
x
位置,显然其权值应该是所有满足
对于权值第
i
小的位置,前面
dp[i][j]
表示前
i
个位置左括号比右括号多
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 205
int n,m,val[maxn],id[maxn];
ll k,dp[maxn][maxn];
char ans[maxn];
bool cmp(int a,int b)
{
return val[a]<val[b];
}
int main()
{
while(~scanf("%d%d%I64d",&n,&m,&k))
{
int N=n+m-1;
for(int i=1;i<=N;i++)val[i]=n*m,id[i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int t;
scanf("%d",&t);
val[i+j-1]=min(val[i+j-1],t);
}
sort(id+1,id+N+1,cmp);
memset(ans,0,sizeof(ans));
for(int l=1;l<=N;l++)
{
ans[id[l]]='(';
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=0;i<N;i++)
for(int j=i&1;j<=i&&j<=N-i;j+=2)
if(dp[i][j])
{
if(dp[i][j]>k)dp[i][j]=k;
if(ans[i+1]!='('&&j)dp[i+1][j-1]+=dp[i][j];
if(ans[i+1]!=')')dp[i+1][j+1]+=dp[i][j];
}
if(k>dp[N][0])
{
k-=dp[N][0];
ans[id[l]]=')';
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)printf("%c",ans[i+j-1]);
printf("\n");
}
}
return 0;
}