qduoj 26 排在第几个 康拓展开
来源:互联网 发布:mysql密码策略设置 编辑:程序博客网 时间:2024/04/28 02:23
题目链接
感谢:http://blog.csdn.net/zhongkeli/article/details/6966805
给你一个排列让你求他是在当前字典序全排列的第几个
康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! 其中,ai为当前未出现的元素中是排在第几个(从0开始)。(求排在第几个,只需要找到当前未出现的有多少比他小的就可以了)
这个公式可能看着让人头大,最好举个例子来说明一下。例如,有一个数组 s = ["A", "B", "C", "D"],它的一个排列 s1 = ["D", "B", "A", "C"],现在要把 s1 映射成 X。n 指的是数组的长度,也就是4,所以
X(s1) = a4*3! + a3*2! + a2*1! + a1*0!
关键问题是 a4、a3、a2 和 a1 等于啥?
a4 = "D" 这个元素在子数组 ["D", "B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,"D"是第3大的元素,所以 a4 = 3。
a3 = "B" 这个元素在子数组 ["B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,所以 a3 = 1。
a2 = "A" 这个元素在子数组 ["A", "C"] 中是第几大的元素。"A"是第0大的元素,"C"是第1大的元素,所以 a2 = 0。
a1 = "C" 这个元素在子数组 ["C"] 中是第几大的元素。"C" 是第0大的元素,所以 a1 = 0。(因为子数组只有1个元素,所以a1总是为0)
所以,X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20
#include<bits/stdc++.h>#define Ri(a) scanf("%d", &a)#define Rl(a) scanf("%lld", &a)#define Rf(a) scanf("%lf", &a)#define Rs(a) scanf("%s", a)#define Pi(a) printf("%d\n", (a))#define Pf(a) printf("%lf\n", (a))#define Pl(a) printf("%lld\n", (a))#define Ps(a) printf("%s\n", (a))#define W(a) while(a--)#define CLR(a, b) memset(a, (b), sizeof(a))#define MOD 1000000007#define inf 0x3f3f3f3f#define exp 0.00000001#define pii pair<int, int>#define mp make_pair#define pb push_backusing namespace std;typedef long long ll;const int maxn=1e5+10;ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}string s;int f[111];void init(){CLR(f,0);f[0]=f[1]=1;for(int i=2;i<=15;i++)f[i]=i*f[i-1];return ;}int main(){init();while(cin>>s){int cnt=0;ll ans=0;for(int i=0;i<s.size();i++){cnt=0;for(int j=i+1;j<s.size();j++){if(s[j]<s[i])cnt++;}ans+=cnt*f[s.size()-1-i];}Pl(ans+1);//康拓展开都是从0开始的 }return 0;}
通过康托逆展开生成全排列
如果已知 s = ["A", "B", "C", "D"],X(s1) = 20,能否推出 s1 = ["D", "B", "A", "C"] 呢?
因为已知 X(s1) = a4*3! + a3*2! + a2*1! + a1*0! = 20,所以问题变成由 20 能否唯一地映射出一组 a4、a3、a2、a1?如果不考虑 ai 的取值范围,有
3*3! + 1*2! + 0*1! + 0*0! = 20
2*3! + 4*2! + 0*1! + 0*0! = 20
1*3! + 7*2! + 0*1! + 0*0! = 20
0*3! + 10*2! + 0*1! + 0*0! = 20
0*3! + 0*2! + 20*1! + 0*0! = 20
等等。但是满足 0 <= ai <= n-1 的只有第一组。可以使用辗转相除的方法得到 ai,如下图所示:
知道了a4、a3、a2、a1的值,就可以知道s1[0] 是子数组["A", "B", "C", "D"]中第3大的元素 "D",s1[1] 是子数组 ["A", "B", "C"] 中第1大的元素"B",s1[2] 是子数组 ["A", "C"] 中第0大的元素"A",s[3] 是子数组 ["C"] 中第0大的元素"C",所以s1 = ["D", "B", "A", "C"]。
#include<bits/stdc++.h>#define Ri(a) scanf("%d", &a)#define Rl(a) scanf("%lld", &a)#define Rf(a) scanf("%lf", &a)#define Rs(a) scanf("%s", a)#define Pi(a) printf("%d\n", (a))#define Pf(a) printf("%lf\n", (a))#define Pl(a) printf("%lld\n", (a))#define Ps(a) printf("%s\n", (a))#define W(a) while(a--)#define CLR(a, b) memset(a, (b), sizeof(a))#define MOD 1000000007#define inf 0x3f3f3f3fusing namespace std;typedef long long ll;const int maxn=1e5+10;string s="12345";int f[10];int vis[10];char ss[11];void init(){CLR(f,0);f[0]=1;for(int i=1;i<10;i++)f[i]=f[i-1]*i;return ;}int main(){init();int n,m;Ri(n);m=s.size();CLR(vis,0);n--;//康拓展开都是从第0个开始的for(int i=1;i<=m;i++){//Pi(1);int k=f[m-i];//Pi(k);int w=n/k+1;//这里加1是为了找到第0个的那种情况.n%=k;int j=0;while(w){if(!vis[j])w--;j++;}ss[i]=s[j-1];vis[j-1]=1;}ss[m+1]='\0';Ps(ss+1);return 0; }
- qduoj 26 排在第几个 康拓展开
- QDUOJ 26 - 排在第几个
- qduoj 排在第几个
- QDU第二次月赛 排在第几个(康拓展开)
- nyoj 139 我排第几个--康拓展开
- NYOJ 139 我排第几个 [康拓展开]【数学】
- 康拓展开 之 我排第几个
- nyoj 139 我排第几个(康拓展开)
- nyoj139 我排第几个(康拓展开)
- NYOJ 139 我排第几个 和 NYOJ 143 第几是谁? 【康拓展开和逆康拓展开】
- 我排第几个---康托展开
- 我排第几个?
- 我排第几个
- 我排第几个
- 我排第几个
- 我排第几个
- 排第几个
- 我排第几个
- 现阶段学习目标
- AngularJS_ng-repeat改变某个的背景颜色
- MyBatis框架中的事务处理
- linux下如何识别ntfs文件类型的硬盘
- git的常见命令
- qduoj 26 排在第几个 康拓展开
- HTML自学教程--一天搞定HTML(总结)
- 移动端开发--日记
- 定时任务优化
- 网络传输过程中的字节序列问题
- Zabbix-3.0.4 添加触发器
- linux-rpm
- Builder模式
- sql拼接,String和Stringbuffer的问题