UVA11077——dp+循环(群论)

题目链接:https://vjudge.net/contest/332708#problem/P

题意:

Sorting is one of the most used operations in real life, where Computer Science comes into act. It is well-known that the lower bound of swap based sorting is nlog(n). It means that the best possible sorting algorithm will take at least O(nlog(n)) swaps to sort a set of n integers. However, to sort a particular array of n integers, you can always find a swapping sequence of at most (n−1) swaps, once you know the position of each element in the sorted sequence.

For example consider four elements <1 2 3 4>. There are 24 possible permutations and for all elements you know the position in sorted sequence.

If the permutation is <2 1 4 3>, it will take minimum 2 swaps to make it sorted. If the sequence is <2 3 4 1>, at least 3 swaps are required. The sequence <4 2 3 1> requires only 1 and the sequence <1 2 3 4> requires none. In this way, we can find the permutations of N distinct integers which will take at least K swaps to be sorted.
Input

Each input consists of two positive integers N (1 ≤ N ≤ 21) and K (0 ≤ K < N) in a single line. Input is terminated by two zeros. There can be at most 250 test cases.
Output
For each of the input, print in a line the number of permutations which will take at least K swaps.
Sample Input
3 1 3 0 3 2 0 0
Sample Output
3 1 2

题意理解:

排序是现实生活中最常用的操作之一,计算机科学就是在这里产生的。众所周知,交换排序的下界是nlog(n)。这意味着最好的排序算法至少需要O(nlog(n))个交换来对一组n个整数进行排序。但是,要对n个整数的特定数组进行排序,只要知道排序序列中每个元素的位置,就可以始终找到最多(n-1)个交换序列。例如,考虑四个元素<1 2 3 4>。有24种可能的排列,对于所有元素,您都知道排序顺序中的位置。如果置换<2 1 4 3>,则至少需要2个交换才能将其排序。如果序列<2 3 4 1>,则至少需要3个交换。序列<4 2 3 1>只需要1,序列<1 2 3 4>不需要。这样,我们就可以找到N个不同整数的排列,至少需要K个交换来排序。

题解:LRJ大白书上的例题,代码上有详解

#include 
#include 
#include 
#include 
#define rp(i, s, t) for (i = s; i <= t; i++)
#define RP(i, s, t) for (i = t; i >= s; i--)
#define ll long long
#define ull unsigned long long
using namespace std;
inline int read()
{
    int x = 0, t = 1;
    char ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-')
        ch = getchar();
    if (ch == '-')
        t = -1, ch = getchar();
    while (ch <= '9' && ch >= '0')
        x = x * 10 + ch - 48, ch = getchar();
    return x * t;
}
inline void write(int x)
{
    char F[200];
    int tmp = x > 0 ? x : -x;
    if (x < 0)
        putchar('-');
    int cnt = 0;
    while (tmp > 0)
    {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0)
        putchar(F[--cnt]);
}
//需要提前知道给一个序列P,至少需要交换几次才能变成{1,2,3,....n}的次数也等于从{1,2,3,...n}序列交换至序列P的最少次数(相反的,很好想)
//而且可以知道一个规律,交换c个元素的循环需要c-1次。
ull f[30][30];
//状态:f[i][j]表示满足“至少需要交换j次才能变成{1,2,3,4,....i}”的排列的个数
//状态转移方程: f[i][j]=f[i-1][j]+f[i-1][j-1]*(i-1)(白书上的解释递推方程好像错了,QWQ!)
//因为要么元素i自己形成一个循环(那么元素i就不浪费交换次数了,只需要j次交换变成{1,2,3,....i-1}的排列)
//要么加入前面任意一个循环的任意一个位置(加入循环代表消耗了一次交换次数,同时交换j-1次变成{1,2,3,....i-1}的排列),而且为了交换i个元素的循环需要(i-1)次)
//边界条件:f[1][0]=1,f[1][j]=0;
void init()
{
    memset(f, 0, sizeof f);
    int i, j;
    f[1][0] = 1; //边界处理
    rp(i, 2, 21)
    {
        rp(j, 0, i - 1)
        {
            f[i][j] = f[i - 1][j]; //元素i自己形成一个循环
            if (j > 0)
                f[i][j] += (i - 1) * f[i - 1][j - 1]; //加入前面任意一个循环的任意一个位置
        }
    }
}
int main()
{
    int n, k;
    init();
    while (n = read(), k = read())
    {
        if (!n && !k)
            break;
        printf("%llu\n", f[n][k]);
    }
    return 0;
}

 

 

你可能感兴趣的:(组合数学——群论)