Archive for category ACM

递推求解专题练习(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)[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