ACM递推求解入门题(附代码解释)

目录

HDU2048 神、上帝以及老天爷(错排公式)

HDU2047 阿牛的EOF牛肉串

HDU2045 不容易系列之(3)—— LELE的RPG难题

HDU2563 统计问题 

HDU2046 骨牌铺方格

HDU 2050 折线分割平面

HDU 2709 Sumsets

HDU 1098 Ignatius's puzzle


HDU2048 神、上帝以及老天爷(错排公式)

题解:这个题主要考错排公式,不懂可以看这篇博客传送门

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>

using namespace std;

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        long long sum = 1, a[21] = {0, 0, 1};
        scanf("%d", &n);
        for (int i = n; i > 0; i--)
            sum *= i;
        for (int i = 3; i <= n; i++)
            a[i] = (i - 1) * (a[i - 1] + a[i - 2]);//排错公式
        printf("%.2f%%\n", (float) a[n] * 100 / sum);
    }
    return 0;
}

 

HDU2047 阿牛的EOF牛肉串

 

题解:这个题主要讨论两种情况, 对于第n格取“O”的情况,为了保证两个“O”不相邻,n-1格有两种可能,即“E”、“F”。对于余下的n-2格,由于第n-1格不取“O”,所以第n-2格不受n-1格的限制。其排列数等于f(n-2)。对于第n格不取“O”的情况,即取“E”、“F”。对于余下的n-1格,由于第n格不取“O”,所以,第n-1格不受n格的限制。其排列数等于f(n-1)。

递推式为:a[i]=2*(a[i-1]+a[i-2])

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

int main() {
    int n;
    long long a[100] = {0, 3, 8};
    while (scanf("%d", &n) != EOF) {
        for (int i = 3; i <= n; i++)
            a[i] = 2 * (a[i - 1] + a[i - 2]);
        printf("%lld\n", a[n]);
    }
    return 0;
}


HDU2045 不容易系列之(3)—— LELE的RPG难题

题解:这是一个递推问题。数组 nums [ n ] 保存 n 个格子有多少种涂法。

    n 个格子的涂法可以由 n - 1 个格子的涂法再加 1 个格子得到。n - 1 个格子涂好后,再加 1 个格子就只能涂 1 种颜色,所以nums [ n ] = nums [ n - 1 ] * 1。由 n - 1 个格子递推到 n 个格子的时候,会出现一个问题:原来 n - 1 个格子的首尾两个格子不能同色,加 1 个格子后,原来的 n - 1 个格子的首尾两个格子可以同色了。在 n 个格子出现问题的基础上,反推可知:n - 1 个格子首尾同色的时候,n - 2 个格子肯定合法!所以,n - 1 个格子首尾同色,再加 1 个格子就可以涂 2 种颜色,所以nums [ n ] = num [ n - 2 ] * 2

    综上所述:nums [ 1 ] = 3 ; nums [ 2 ] = 6 ; nums [ 3 ] = 6 ; nums [ n ] = nums [ n - 1 ] * 1 + nums [ n - 2 ] * 2。

#include<iostream>
#include<algorithm>

using namespace std;

int main() {
    int n;
    long long a[51] = {0, 3, 6, 6};
    for (int i = 4; i < 51; i++)
        a[i] = a[i - 1] + 2 * a[i - 2];
    while (scanf("%d", &n) != EOF)
        printf("%lld\n", a[n]);
    return 0;
}

 

HDU2563 统计问题 

题解:f(n)走的步数不仅和f(n-1)有关,还和f(n-2)有关,从f(n-1)->f(n)的过程中会有f(n-2)个三个方向的,因为从f(n-2)->f(n-1)中每一次都会产生一步向上的,只有向上走下一步才能有三种走法,否则两种,所以把三次的减去就得到两次的了,然后依次调用下去。方程是:f(n)=f(n-2)*3+(f(n-1)-f(n-2))*2  =>f(n)=f(n-2)+f(n-1)*2。

#include<stdio.h>
#include<iostream>
#include<string.h>

using namespace std;

int main() {
    long long a[21] = {0, 3, 7};
    for (int i = 3; i < 21; i++)
        a[i] = 2 * a[i - 1] + a[i - 2];
    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        printf("%lld\n", a[n]);
    }
    return 0;
}

HDU2046 骨牌铺方格

题解:假设用arr[i]表示2*i的方格一共有组成的方法数,我们知道arr[1]=1;arr[2]=2;现在假设我们已经知道了arr[i-1]和arr[i-2],求arr[i],所谓arr[i],不过是在2*(i-1)的格子后边加上一格2*1的方格罢了,骨牌在这一格上横着放,竖着放,如果前面i-1块已经铺好,则第i块只有一种铺法,就是竖着放,如果要横着放,也只有一种铺法,不过要求前面i-2块已经铺好!因此arr[i]=arr[i-1]+arr[i-2]。

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

int main() {
    long long a[51] = {0, 1, 2, 3};
    int n;
    for (int i = 4; i < 51; i++)
        a[i] = a[i - 1] + a[i - 2];
    while (scanf("%d", &n) != EOF)
        printf("%lld\n", a[n]);
    return 0;
}

HDU 2050 折线分割平面

解题:递推递推,先分析下直线分割平面的情况,增加第n条直线的时候,跟之前的直线最多有n-1个交点,此时分出的部分多出了(n-1)+1。折线也是同理,f(1)=2,f(2)=7,先画好前面n-1条折线,当增加第n条,折线时,此时与图形新的交点最多有2*2(n-1)个,所以分出的部分多出了2*2(n-1)+1,所以推出f(n)=f(n-1)+4*(n-1)+1,n>=3。推导可以看看这篇博客传送门

#include<stdio.h>

int main() {
    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        printf("%d\n", 2 * n * n - n + 1);
    }
    return 0;
}

HDU 2709 Sumsets

题解:当n是奇数的时候,n可以由前面的偶数的所有情况+1得到,所以当n为偶数的时候a[i]=a[i-1];

当n为偶数的时候它的组合含有1的部分可由比它小的奇数得到,偶数部分可由n/2的组合得到。以6为例子,6的组合有(1,1,1,1,1,1),(1,1,1,1,2),(1,1,2,2),(2,2,2),(1,1,4),(2,4),组合里有一的组合(1,1,1,1,1,1),(1,1,1,1,2),(1,1,2,2),(1,1,2,2),都是5的每个组合加一得到的所以这部分是a[i]=a[i-1],不含有一的组合(2,2,2),(2,4),就是3的组合数每个乘以2得到的,所以这部分是a[i]=a[i/2];,综上,偶数部分的代码是转移方程,a[i]=(a[i-1]+a[i/2])%mod;。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>

const int maxn = 1e6 + 5;
const int mod = 1e9;
#define me(a) memset(a,0,sizeof(a))
#define ll long long
using namespace std;
ll a[maxn] = {0, 1, 2};

int main() {
    int n;
    cin >> n;
    for (int i = 3; i <= n; i++) {
        if (i % 2 != 0)
            a[i] = a[i - 1];
        else
            a[i] = (a[i - 1] + a[i / 2]) % mod;
    }
    cout << a[n] << endl;
    return 0;
}

HDU 1098 Ignatius's puzzle

题解:我们可以得到 f(1)=18+k*a;题中求最小的a满足条件,把f(x+1),按二项式展开得f(x+1 ) = f (x) +  5*( (13  1 ) x^12 ...... .....+(13  13) x^0  )+  13*(  (5  1 )x^4+...........+ ( 5  5  )x^0  )+k*a;很容易证明,除了5*(13  13) x^0 、13*( 5  5  )x^0 和k*a三项以外,其余各项都能被65整除,那么也只要求出18+k*a能被65整除就可以了.。而f(1)也正好等于18+k*a。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;

int main() {
    int k;
    while (~scanf("%d", &k)) {
        int flog = 0, x = 0;
        for (int i = 0; i < 65; i++) {
            if ((18 + k * i) % 65 == 0) {
                flog = 1;
                x = i;
                break;
            }
        }
        if (flog)
            cout << x << endl;
        else
            cout << "no" << endl;
    }
    return 0;
}

 

©️2020 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值