题意
给出两个6行5列的字母矩阵,一个密码满足:密码的第i个字母在两个字母矩阵的第i列均出现。
然后找出字典序为k的密码,如果不存在输出NO。k≤7777
分析
由于k的范围很小,首先可以考虑dfs
但是这个题其实是在考康托展开
先把每个位置能填的数全部预处理出来,比如样例中每一位可选的分别是{ACDW}、{BOP}、{GMOX}、{AP}、{GSU}
那就是4*3*4*2*3=288种
来模拟一下如何解码,比如k=197,注意标红的数字
3*4*2*3=72 4*2*3=24 2*3=6 3=3 1
197÷72=2……53 所以第一位选D
53÷24=2……5 所以第二位选P
5÷6=0……5 所以第三位选G
5÷3=1……2 所以第四位选P
2÷1=2……0 所以第五位选U
就能得到DPGPU,而我们在程序中模拟这个过程。处理出每个后缀乘积,并用上述方式得到的商+1就是选的位数,再用余数继续算。
调了好久,此题有坑,同一列中一个字母可以重复出现,所以别用map!!!
代码
#include<bits/stdc++.h> using namespace std; int T,k; char x; vector<char> ok[7]; int mul[7],vis[7][128]; int main() { scanf("%d",&T); while(T--) { memset(vis,0,sizeof(vis)); for(int i=1;i<=5;i++)ok[i].clear(); cin>>k; for(int i=1;i<=6;i++) for(int j=1;j<=5;j++) { cin>>x; vis[j][x]=1; } for(int i=1;i<=6;i++) for(int j=1;j<=5;j++) { cin>>x; if(vis[j][x]) { ok[j].push_back(x); vis[j][x]=0; } } for(int i=1;i<=5;i++) sort(ok[i].begin(),ok[i].end()); mul[5]=1; for(int i=4;i>=0;i--) mul[i]=mul[i+1]*ok[i+1].size(); if(k>mul[0]) cout<<"NO"<<endl; else { k--; for(int i=1;i<=5;i++) { int s=k/mul[i]; cout<<ok[i][s]; k=k%mul[i]; } cout<<endl; } } return 0; }