排列字典序算法

今天学校圣诞编程组队网络赛出了道排列题,于是在这里记录一下解决全排列及第N个排列问题的字典序算法。
字典序算法中,对于数字1、2、3……n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列12354和12345,排列12345在前,排列12354在后。按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是54321。
字典序算法如下:
设P是1~n的一个全排列:P = P1P2……Pn = P1P2……Pj-1PjPj+1……Pk-1PkPk+1……Pn
(1)从排列的最右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi (2)在Pj的右边的数字中,找出所有比Pj大的数中最小的数字Pk,即 k=max{i|Pi>Pj}(右边的数从右至左是递增的,因此k是所有大于Pj的数字中序号最大者)
(3)对换Pj,Pk
(4)再将Pj+1……Pk-1PkPk+1Pn反转,即得到排列P的下一个排列。

以下是模拟STL函数next_permutation(num,num+n)实现的代码:


#include <iostream>
#include <algorithm>
using namespace std;
int n,m,i;
int num[1024];
bool next(int n)
{
    int j = -1;
    for (int i = n-2;i >= 0;i--)
        if (num[i] < num[i+1])
        {
            j = i;
            break;
        }//找到第一个比自己后面的元素小的元素,赋给j
    if (j < 0)
        return 0;
    int k;
    for (int i = n-1;i > j;i--)
        if (num[i] > num[j])
        {
            k = i;
            break;
        }//找到右边降序排列中最右边的大于自己的元素,将其位置赋给k
    swap(num[j],num[k]);//交换num[j]和num[k],右边得到一个新的降序排列的序列
    reverse(num+j+1,num+n);//反转右边的序列,使其升序排列
    return 1;
}
int main()
{
    while (scanf("%d%d",&n,&m) != EOF && n && m)
    {
        for (i = 0;i < n;i++)
        {
            //scanf("%d", &num);
            num[i] = i+1;
        }
        sort(num,num+n);
        for (i = 1;i < m;i++)
            next(n);//模拟STL函数next_permutation(num,num+n);
        for (i = 0;i < n;i++)
        {
            printf("%d", num[i]);
            if (i == n-1)
                printf("\n");
        }
    }
    return 0;
}

附上能用next_permutation()函数解决的题:PKU11461256173118333187
HDU1027

,

No Comments

递推求解专题练习(For Beginner)[6]&HDU2049

其实HDU2048就是这题的弱化版,这题只是把错排巧妙的加进去了。

考虑n对夫妇,从中挑出m位,再进行错排,那题目就可以解决了。错排在上一篇日志有讲了,而n选m则是典型的组合数学,由于这是递推求解专题练习,所以我们采用递推的方式来计算组合数和错排数。
递推公式如下:


f[n][m] = c[n][m] * d[m];
c[n][m] = c[n-1][m] + c[n-1][m-1];//组合数C(n,m)
d[m] = (m-1)*(d[m-1]+d[m-2]);//错排数D[m]

其实计算错排数在n比较小的时候(大约小于13)使用近似公式

D(n)=[n!/e+0.5]

([x]是高斯函数,表示不超过x的最大整数)会比较好,况且这题是离线的题,我们也完全可以自己计算20以内的错排数来打表。不过这些就不是题目的本意了。

代码如下:


/*
ID:Shuaicpp
Prog:HDU2049-不容易系列之(4)——考新郎
Lang:C++
Algorithm:DP-f[n][m] = c[n][m] * d[m];
             c[n][m] = c[n-1][m] + c[n-1][m-1];
             d[m] = (m-1)*(d[m-1]+d[m-2]);
*/
#include <iostream>
using namespace std;
const int MAXN = 21;
long long c[MAXN][MAXN],d[MAXN];
int main()
{
    int n,m,k;
    d[1] = 0;
    d[2] = 1;
    for (int i = 3;i < MAXN;i++)
        d[i] = (i-1)*(d[i-1]+d[i-2]);
    for (int i = 0;i < MAXN;i++)
    {
        c[i][0] = 1;
        c[i][1] = i;
    }
    for (int i = 2;i < MAXN;i++)
        for (int j = 2;j <= i;j++)
            c[i][j] = c[i-1][j-1] + c[i-1][j];
    cin >> k;
    for (int i = 0;i < k;i++)
    {
        cin >> n >> m;
        cout << c[n][m] * d[m] << endl;
    }
    return 0;
}

, ,

1 Comment

递推求解专题练习(For Beginner)[5]&HDU2048

N张字条的所有排列可能自然是A(N,N)= N!种排列方式
现在的问题就是N张字条的错排方式有几种。分两种情况讨论
①:如果前面N-1个人拿的都不是自己的字条,即前N-1个人满足错排,那么只要第N个人把自己的票与前面N-1个人中的任意一个交换,就可以满足N个人的错排。这时有f(N-1)种方法。

②:如果前N-1个人不满足错排,而第N个人把自己的字条与其中一个人交换后恰好满足错排。即在前面N-1人中,有N-2个人满足错排,有且只有一个人拿的是自己的字条,而第N个人恰好与他做了交换,这时候就满足了错排。这时有f(n-2)种方法

对于①,因为前N-1个人中,每个人都有机会与第N个人交换,所以有N-1种交换的可能。
对于②,因为前N-1个人中,每个人都有机会拿着自己的字条。所以也有N-1种交换的可能。

所以得错排递推公式

D[n] = (n-1)*(D[n-1]+D[n-2])

由于计算n!和D[n]数字会非常大,所以我们采用边做边除而不是先算D(n),再除n!的方法。


已知D[n]=(n-1)(D[n-1]+D[n-2]);
令f[n]=D[n]/n!;则有D[n]=n!*f[n];
代入可得f[n]=(n-1)/n!*(f[n-1]*(n-1)!+f[n-2]*(n-2)!);
整理有f[n]=(n-1)/n*(f[n-1]+f[n-2]);
即得到错排概率公式f[n]=(n-1)/n*(f[n-1]+f[n-2]);

代码如下:


/*
ID:Shuaicpp
Prog:HDU2048-神、上帝以及老天爷
Lang:C++
Algorithm:DP-f[n] = ((n-1)*f[n-1]+f[n-2]) / n;
*/
#include <iostream>
#include <iomanip>
using namespace std;
const int MAXN = 20;
double f[MAXN];
int main()
{
    int n,x;
    f[1] = 0;
    f[2] = 0.5;
    for (int i = 3;i <= MAXN;i++)
        f[i] = ((i-1)*f[i-1]+f[i-2]) / i;
    cin >> n;
    for (int i = 0;i < n;i++)
    {
        cin >> x;
        cout << fixed << setprecision(2) << f[x] * 100 << '%' << endl;
    }
    return 0;
}

, ,

2 Comments

递推求解专题练习(For Beginner)[4]&HDU2047

令E = 0,O = 1, F = 2,则容易知转移方程


f[i][0] = f[i-1][0] + f[i-1][1] + f[i-1][2]; //E
f[i][1] = f[i-1][0] + f[i-1][2];             //O
f[i][2] = f[i-1][0] + f[i-1][1] + f[i-1][2]; //F

边界

f[1][0] = f[1][1] = f[1][2] = 1;

代码如下:


/*
ID:Shuaicpp
Prog:HDU2047-阿牛的EOF牛肉串
Lang:C++
Algorithm:DP-f[i][0] = f[i-1][0] + f[i-1][1] + f[i-1][2]; //E
            f[i][1] = f[i-1][0] + f[i-1][2];             //O
            f[i][2] = f[i-1][0] + f[i-1][1] + f[i-1][2]; //F
*/
#include <iostream>
using namespace std;
const int n = 51;
long long f[n][3] = {0};
int main()
{
   f[1][0] = f[1][1] = f[1][2] = 1;
   for (int i = 2;i < n;i++)
   {
       f[i][0] = f[i-1][0] + f[i-1][1] + f[i-1][2];
       f[i][1] = f[i-1][0] + f[i-1][2];
       f[i][2] = f[i-1][0] + f[i-1][1] + f[i-1][2];
   }
   int x;
   while (cin >> x)
       cout << f[x][0] + f[x][1] + f[x][2] << endl;
   return 0;
}

, ,

No Comments

递推求解专题练习(For Beginner)[3]&HDU2046

如图:

对于给定的2*n格方块,可以由摆放好的2*(n-1)的方块加上一块竖直的2*1方块或者由摆放好的2*(n-2)的方块加上两块横着的2*1方块组合而来。
容易得出状态转移方程为著名的斐波那契数列:

f[n] = f[n-1] + f[n-2],

边界条件

f[1] = 1,f[2] = 2;

代码如下:


/*
ID:Shuaicpp
Prog:HDU2046-骨牌铺方格
Lang:C++
Algorithm:DP-f[i]=f[i-1]+f[i-2];
*/
#include <iostream>
using namespace std;
const int n = 51;
long long f[n] = {0};
int main()
{
   f[1] = 1;
   f[2] = 2;
   f[3] = 3;
   for (int i = 4;i < n;i++)
       f[i] = f[i-1] + f[i-2];
   int x;
   while (cin >> x)
       cout << f[x] << endl;
   return 0;
}

, ,

1 Comment

递推求解专题练习(For Beginner)[2]&HDU2045

第一次的dp思路:

用f[i][j]表示第i位为j色的满足要求的涂法,分三次进行取方案数,则递推方程如下:


f[i][0] = f[i-1][1] + f[i-1][2];(color[1] != 0)
f[i][1] = f[i-1][0] + f[i-1][2];(color[1] != 1)
f[i][2] = f[i-1][0] + f[i-1][1];(color[1] != 2)

ans = (f[n][1] + f[n][2]) + (f[n][0]+f[n][2]) + (f[n][0]+f[n][1]);

更简单的递推思路:
考虑长为n的串,以s[i]表示i位的字符。
1.若前n-1位组成的串合法,则由于首尾不同,再添加一位时,只有1种方法;即s[n] != s[n-1] != s[1]。
2.若前n-1位组成的串不合法,再添加一位后合法,即因为首尾相同而引起的不合法,那么前n-2位组成的串必定合法。此时第n位有2种添加方法。即s[n] != s[n-1] = s[1]。
3.边界条件:f(1)=3;f(2)=6;f(3)=6。


/*
ID:Shuaicpp
Prog:HDU2045-不容易系列之(3)—— LELE的RPG难题
Lang:C++
Algorithm:DP-f[i]=f[i-1]+f[i-2]*2(i>3);边界f[1] = 3,f[2] = 6,f[3] = 6;
*/
#include <iostream>
#include <cmath>
using namespace std;
const int n = 51;
long long f[n] = {0};
int main()
{
    f[1] = 3;
    f[2] = 6;
    f[3] = 6;
    for (int i = 4;i < n;i++)
        f[i] = f[i-1] + f[i-2] * 2;
    int x;
    while (cin >> x)
        cout << f[x] << endl;
    return 0;
}

, ,

No Comments

递推求解专题练习(For Beginner)[1]&HDU2044

如图:递推求解专题练习1
用f[i][j]表示从i到j的路径数,则递推方程如下:

f[i][j]= f[i][j-1] + f[i][j-2];

边界条件

f[i][i] = 1;

/*
ID:Shuaicpp
Prog:HDU2044-一只小蜜蜂...
Lang:C++
Algorithm:DP-f[i][j]= f[i][j-1] + f[i][j-2],边界f[i][i] = 1;
*/
#include <iostream>
#include <cmath>
using namespace std;
const int m = 51;
long long f[m][m] = {0};
int main()
{
    int n;
    cin >> n;
    for (int i = 1;i < m;i++) f[i][i] = 1;
    for (int i = 1;i < m;i++)
        for (int j = i + 1;j < m;j++)
            f[i][j]= f[i][j-1] + f[i][j-2];
    for (int i = 0;i < n;i++)
    {
        int a,b;
        cin >> a >> b;
        cout << f[a][b] << endl;
    }
    return 0;
}

,

No Comments

《算法导论》入手

终于买了本机械工业出版社翻译的《算法导论》(原书第二版),花了63.8+5=78.8大洋,看着这本绝对重量级的算法书,不知道以后的日子会怎么样,反正估计CLRS+刘汝佳的黑书至少能陪伴我即将到来的大一生活,拿在手里真是很能感受到它的分量,既然书名《Introduction to Algorithms》,希望它真的能让我在算法之路上能走得更远一些。

附上几张裸照。

IMG_1506IMG_1505

IMG_1508IMG_1507

目录:

算法导论 第二版(Introduction to Algorithms, Second Edition)

目录(Table of Contents)
前言(Preface)
第一部分(Part I) 基础(Foundations)
第一章 计算中算法的角色(The Role of Algorithms in Computing)
第二章 开始(Getting Started)
第三章 函数的增长率(Growth of Functions)
第四章 递归(Recurrences)
第五章 概率分析与随机化算法(Probabilistic Analysis and Randomized Algorithms)
第二部分(Part II) 排序与顺序统计(Sorting and Order Statistics)
第六章 堆排序(Heapsort)
第七章 快速排序(Quicksort)
第八章 线性时间中的排序(Sorting in Linear Time)
第九章 中值与顺序统计(Medians and Order Statistics)
第三部分(Part III) 数据结构(Data Structures)
第十章 基本的数据结构(Elementary Data Structures)
第十一章 散列表(Hash Tables)
第十二章 二叉查找树(Binary Search Trees)
第十三章 红-黑树(Red-Black Trees)
第十四章 扩充的数据结构(Augmenting Data Structures)
第四部分(Part IV) 高级的设计与分析技术(Advanced Design and Analysis Techniques)
第十五章 动态规划(Dynamic Programming)
第十六章 贪婪算法(Greedy Algorithms)
第十七章 分摊分析(Amortized Analysis)
第五部分(Part V) 高级的数据结构(Advanced Data Structures)
第十八章 B-树(B-Trees)
第十九章 二项式堆(Binomial Heaps)
第二十章 斐波纳契堆(Fibonacci Heaps)
第二十一章 不相交集的数据结构(Data Structures for Disjoint Sets)
第六部分(Part VI) 图算法(Graph Algorithms)
第二十二章 基本的图算法(Elementary Graph Algorithms)
第二十三章 最小生成树(Minimum Spanning Trees)
第二十四章 单源最短路径(Single-Source Shortest Paths)
第二十五章 所有点对的最短路径(All-Pairs Shortest Paths)
第二十六章 最大流(Maximum Flow)
第七部分(Part VII) 精选的主题(Selected Topics)
第二十七章 排序网络(Sorting Networks)
第二十八章 矩阵运算(Matrix Operations)
第二十九章 线性规划(Linear Programming)
第三十章 多项式与快速傅里叶变换(Polynomials and the FFT)
第三十一章 数论算法(Number-Theoretic Algorithms)
第三十二章 字符串匹配(String Matching)
第三十三章 计算几何学(Computational Geometry)
第三十四章 NP-完备性(NP-Completeness)
第三十五章 近似算法(Approximation Algorithms)
第八部分(Part VIII) 附录:数学背景(Mathematical Background)
附录A 求和(Summations)
附录B 集合,等等。(Sets, Etc.)
附录C 计数与概率(Counting and Probability)
参考文献(Bibliography)
索引(Index)

PS:《麻省理工学院-算法导论》(MIT – Introduction to Algorithms)http://www.verycd.com/topics/87348/,里面有电子书和习题答案和教学视频。

配合这里面的MIT的OPEN COURSE使用更好,只不过我不能很流利地听懂,大学要加强英语口语和听力了,就这样了。

, ,

No Comments

syntaxhighlighter2插件修改

由于没有支持数字的高亮,我修改了一下插件使它能够对0-9的数字进行高亮。具体修改方法如下:

修改

plugins\syntaxhighlighter2\filesshBrushCpp.js

(这里以cpp为例,其他依样修改),找到

this.regexList = [

这一行,在这一行上面插入一行

var numbers = '0 1 2 3 4 5 6 7 8 9';

然后找到

{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword bold' }

所在行,在本行最后加入一个逗号,另起一行插入

{ regex: new RegExp(this.getKeywords(numbers), 'gm'), css: 'numbers bold' }

尾部无逗号,保存即可。
接下来修改css使代码实现高亮。

打开

plugins\syntaxhighlighter2\shThemeDefault.css

(其他风格依样修改),在文件末位加入


syntaxhighlighter .numbers,
.syntaxhighlighter .numbers a
{
color: #9933CC !important;
}

就可以对数字显示高亮了,修改后的两个文件打包下载请点这里

No Comments

博客修改记录

为了方便自己和大家,特将修改和安装的插件实时记录。

2009年8月25日

1. 博客刚建立完成,采用了Fusion 3.1 皮肤,作者为 digitalnature

2.添加了若干插件:

[1]中文 WordPress 工具箱,用来解决官方 WordPress 没有照顾到的中文相关问题。使用这个插件,你可以显示随机文章,最新留言(最新引用),留言最多文章,发表评论最多的网友,还有真正的文章摘要,等等,真正截断,没有乱码。

[2]Google XML Sitemaps,这个插件可以为你的网站生一个名为sitemap.xml的文件,方便各大搜索引擎收录您的网站,支持Ask.com, Google, MSN Search and YAHOO。

[3]SyntaxHighlighter2,写代码必备的高亮插件,支持很多种语言,不过我只需要支持cpp就可以了。支持数字高亮请看这里

[4]WP-Cumulus,一个异常漂亮的标签云插件,调用flash完成。

[5]WP-DownloadManager,下载管理器,可以方便地添加修改所需要上传的文件。

[6]WordPress Related Posts,显示相关文章就靠它了。

[7]Google Analytics for WordPress,google的状态统计工具,先去获得你的ID,再设置即可。

[8]Lightbox 2,美观的图片浏览插件。

[9]mimeTeX,方便编辑公式的软件,Example:$$/LaTeX$$

3.制作了新的标题头图片,觉得有点乱。

右边边栏增加了豆瓣我在看的。

No Comments