题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6628
题目:
permutation 1
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Problem Description
A sequence of length n is called a permutation if and only if it's composed of the first n positive integers and each number appears exactly once.
Here we define the "difference sequence" of a permutation p1,p2,…,pn as p2−p1,p3−p2,…,pn−pn−1. In other words, the length of the difference sequence is n−1 and the i-th term is pi+1−pi
Now, you are given two integers N,K. Please find the permutation with length N such that the difference sequence of which is the K-th lexicographically smallest among all difference sequences of all permutations of length N.
Input
The first line contains one integer T indicating that there are T tests.
Each test consists of two integers N,K in a single line.
* 1≤T≤40
* 2≤N≤20
* 1≤K≤min(10^4,N!)
Output
For each test, please output N integers in a single line. Those N integers represent a permutation of 1 to N, and its difference sequence is the K-th lexicographically smallest.
Sample Input
7
3 1
3 2
3 3
3 4
3 5
3 6
20 10000
Sample Output
3 1 2
3 2 1
2 1 3
2 3 1
1 2 3
1 3 2
20 1 2 3 4 5 6 7 8 9 10 11 13 19 18 14 16 15 17 12
解题思路:
n,1,开头的排列对应的difference的第一个数字是最小的=1-n, 8!=40320, 9!=362880,而k最大只取10000。
对于长度为9的排列,当前两位n,1固定,那么后面的7位共有7!=5040种方案,若第一位n固定,后面的8位由8!>10000种方案,可知对于长度>=9的排列,只需在n,1,2,...,n-1这个字典序的基础上再排列min(k,10000)-1次即可求得答案
对于长度≤8的排列,预处理。排列长度一定时,将每个排列对应difference序列转化为char[],sort一下即可,用结构体存数据,询问时根据n,k直接输出。
时间复杂度:预处理O(46232)+T*(10000) = e5
ac代码:
13ms跑完!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=40325;// 8!=40320!!不是10000
int n, k;
struct node{
int num[12];
char c[12];
friend bool operator < (node a, node b)
{
return strcmp(a.c, b.c) < 0;
}
}a[12][maxn];//a[i][j],i:排列长度 j:字典序第j小的difference排列
int b[25] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,};
int ans[30];
void prepare()//预处理n <= 8
{
for(int i = 2; i <= 8; i++)//枚举排列长度
{
int cnt = 1;
do{
for(int j = 1; j <= i; j++) //确定这个排列的num【】和c【】
{
a[i][cnt].num[j] = b[j];//b从1开始存
if(j > 1) a[i][cnt].c[j - 2] = b[j] - b[j - 1] + 'a';//c从0开始存,方便排序
}
a[i][cnt].c[i - 1] = '\0'; //!!!
cnt++;
}while(next_permutation(b+1, b+1+i));
sort(a[i] + 1, a[i] + cnt);//排名从1开始
}
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
int n, k, t;
scanf("%d", &t);
prepare();
while(t--)
{
scanf("%d %d", &n, &k);
k = min(k, 10000);
if(n > 8)
{
ans[1] = n;
for(int i = 2; i <= n; i++)
ans[i] = i - 1;//字典序最小的排列已经得出
for(int i = 1; i < k; i++) // 还需排k-1次
next_permutation(ans + 1, ans + 1 + n);
for(int i = 1; i <= n; i++)
{
if(i > 1) printf(" ");
printf("%d", ans[i]);
}
printf("\n");
}
else
{
for(int i = 1; i <= n; i++)
{
if(i > 1) printf(" ");
printf("%d", a[n][k].num[i]);
}
printf("\n");
}
}
return 0;
}