一堆递推题

目录

  • 一堆递推题

    • P1367【训练题】爬楼梯[2]

      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 提示
      • 思路
    • 铺瓷砖
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 思路
    • 城市路径
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 提示
      • 思路
    • 彩带
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 提示
      • 思路
    • 斐波那契前N项和
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 提示
      • 思路
    • 偶数个3
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 思路
    • 回文拆分
      • 描述
      • 輸入
      • 輸出
      • 輸入範例 1
      • 輸出範例 1
      • 思路

一堆递推题

同步:<https://buringstraw.win/index.php/archives/27/ >

——来自义冢OJ和义冢OJ的contests

P1367【训练题】爬楼梯[2]

描述

  何老师爬楼梯,他可以每步上 1 、2或3 级,输入楼梯的级数,求不同的走法数。例如:楼梯一共有3级,他可以每步都走一级,或者第一步走一级,第二步走两级,也可以第一步走两级,第二步走一级,还有就是第一步就上3级,所以一共4种方法。

  但不幸的是,楼梯上有K级坏了,何老师不能踩在这些楼梯上,现在给出楼梯的级数N和坏了的K级楼梯,请你计算他上楼梯的方法总数。

輸入

  第一行:N、K。  第二行:K个整数h[i],表示坏了的楼梯的级数(1<=h[i]<=N)。

輸出

  不同的走法数,这个数字可能很巨大,所以输出最后答案mod 1234567。

輸入範例 1

5 2
2 4

輸出範例 1

2

提示

  1 <= N <= 1000 0 <= k < N

思路

把所有有坑的楼梯的方案数在递推过程中设为0

注意有可能最前面几级也有坑

所以用0作为边界

中途检查是否越界

#include<cstdio>
#include<iostream>
using namespace std;

const int MAXN=1005;

int f[MAXN];
bool h[MAXN];

int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=k;++i){
        int t;
        cin>>t;
        h[t]=1;
    }
    f[0]=1;
//  f[1]=1;
    for(int i=1;i<=n;++i){
        if(h[i]){
            f[i]=0;
            continue;
        }
        else if(!f[i]){
            if(i-1>=0)f[i]+=f[i-1];
            if(i-2>=0)f[i]+=f[i-2];
            if(i-3>=0)f[i]+=f[i-3];
            f[i]%=1234567;
        }
    }
    cout<<f[n]<<endl;
    return 0;
}

铺瓷砖

描述

用红色的 1×1 和黑色的 2×2 两种规格的瓷砖不重叠地铺满 n×3 的路面,求出有多少种不同的铺设方案。

輸入

一行一个整数 n,0<n<1000。

輸出

一行一个整数,为铺设方案的数量模12345的结果。

輸入範例 1

2

輸出範例 1

3

思路

从之前一格开始填有一种方法

从之前两格开始填有3种方法

但是全用1x1包含在从之前一格开始填里面

所以f[i]=f[i-1]+f[i-2]*2

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

#include<cstdio>
#include<iostream>
using namespace std;

int f[1005];

int main(){
    int n;
    cin>>n;
    f[0]=f[1]=1;
    //f[2]=3;
    for(int i=2;i<=n;++i){
        f[i]=f[i-1]+f[i-2]*2;
        f[i]%=12345;
    }
    cout<<f[n]<<endl;
    return 0;
}

城市路径

描述

地图上有 n 个城市,一只奶牛要从 1 号城市开始依次经过这些城市,最终到达 n 号城市。但是这只奶牛觉得这样太无聊了,所以它决定跳过其中的一个城市(但是不能跳过 1 号和 n 号城市),使得它从 1 号城市开始,到达 n 号城市所经过的总距离最小。假设每一个城市 i 都有一个坐标(x i ,y i ),从 (x 1 ,y 1 ) 的城市 1 到 (x 2 ,y 2 ) 的城市 2 之间的距离为 | x 1 -x 2 | + | y 1 -y 2 | 。

輸入

第 1 行 1 个正整数 n,表示城市个数。接下来的 n 行,每行 2 个数 x i 和 y i ,表示城市 i 的坐标。

輸出

一行一个数,使得它从1号城市开始,跳过某一个城市,到达n号城市所经过的最小总距离。

輸入範例 1

4
0 0
8 3
11 -1
10 0

輸出範例 1

14

提示

【样例说明】跳过 2 号城市。【数据规模】对于 40% 的数据满足:n≤1000。对于 100% 的数据满足:3≤n≤100000,-1000≤x i ,y i ≤1000。

思路

有的题,表面上是一道递推题,实际上可以用模拟做
? ——鲁迅

这是贪心

? ——hqxperisino大佬

比较每一个点删除之后对路程的影响,选出删除之后最短的

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;

const int MAXN=1e5+5;

struct zb{
    int x,y;
} city[MAXN];
int n;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&city[i].x,&city[i].y);
    }
    int maxs=0,maxi=0;
    int he=0;

    for(int i=2;i<n;++i){
        int tmp=
            abs(city[i+1].x-city[i].x)+abs(city[i+1].y-city[i].y)
            +abs(city[i-1].x-city[i].x)+abs(city[i-1].y-city[i].y);
        tmp-=abs(city[i+1].x-city[i-1].x)+abs(city[i+1].y-city[i-1].y);
        if(tmp>maxs){
            maxi=i;
            maxs=tmp;
        }
        he+=abs(city[i-1].x-city[i].x)+abs(city[i-1].y-city[i].y);
    }
    he+=abs(city[n-1].x-city[n].x)+abs(city[n-1].y-city[n].y);
    he-=maxs;
    printf("%d\n",he);
    return 0;
}

彩带

描述

一中 90 周年校庆,小林准备用一些白色、蓝色和红色的彩带来装饰学校超市的橱窗,他希望满足以下两个条件:

(1) 相同颜色的彩带不能放在相邻的位置;

(2) 一条蓝色的彩带必须放在一条白色的彩带和一条红色的彩带中间。

现在,他想知道满足要求的放置彩带的方案数有多少种。

例如,如图 9.4-1 所示为橱窗宽度n=3 的所有放置方案,共 4 种。

輸入

一行一个整数 n,表示橱窗宽度(或者说彩带数目)。

輸出

一行一个整数,表示装饰橱窗的彩带放置方案数。

輸入範例 1

3

輸出範例 1

4

提示

对 30% 的数据满足:1≤n≤15。对 100% 的数据满足:1≤n≤45。

思路

此题感谢perisino大佬的指点

f[i]为彩带数目为i时的方案数

注意最后一条不能是蓝色

  • (i-1)条为红色或白色:因为最后一条不能是蓝色,所以有一种情况
  • (i-1)条为蓝色:第i条与第(i-2)条颜色相反,有一种情况
  • 大佬之前把第二个(i-1)写成了(i-2)我差点没看懂

所以f[i]=f[i-1]+f[i-2]

#include<cstdio>
#include<iostream>
using namespace std;

const int MAXN=50;

int f[MAXN];
int n;

int main(){
    f[1]=2;
    f[2]=2;
    cin>>n;
    for(int i=3;i<=n;++i){
        f[i]=f[i-1]+f[i-2];
    }
    cout<<f[n]<<endl;
    return 0;
}

斐波那契前N项和

描述

求斐波那契数列前n项和取模m

輸入

一行两个整数n m

輸出

输出前n项和取模m

輸入範例 1

5 1000

輸出範例 1

12

提示

1<=n<=2*10^91<=m<=1000000010

思路

是前n项和不是第n项

数据范围非常大,用矩阵

矩阵快速幂都忘了,丢人

矩阵A:1x3

? 内容:f(i-1),f(i-2),g(i-1)

? 其中f(i)表示斐波那契数列的第i项,g(i)表示前i项的和

矩阵B:3x3

? 内容:

        1 1 1
        1 0 1
        0 0 1

则 \(f(n)=A\cdot B^{(n-2)}\)

要用long long要用long long要用long long

#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;

const LL MAXN=5;

LL n,m;

struct juzhen{
    private:
        LL v[MAXN][MAXN];
        LL h,l;

        void prLL(void){
            for(LL i=1;i<=h;++i){
                for(LL j=1;j<=l;++j){
                    cout<<v[i][j]<<' ';
                }
                cout<<'\n';
            }
        }

    public:
        juzhen(LL he,LL le){
            memset(v,0,sizeof(v));
            h=he,l=le;
        }

        friend juzhen operator *(juzhen a,juzhen b){
            juzhen c(a.h,b.l);
            for(LL i=1;i<=a.h;++i){
                for(LL j=1;j<=b.l;++j){
                     for(LL k=1;k<=a.l;++k){
                        c.v[i][j]+=(a.v[i][k]*b.v[k][j]%m);
                        c.v[i][j]%=m;
                     }
                }
            }
            return c;
        }

        friend juzhen operator ^(juzhen a,LL b){

            juzhen ret(a.h,a.l),sum=a;
            for(LL i=1;i<=a.h;++i){
                ret.v[i][i]=1;
            }
            while(b){
                if(b&1){
                    ret=ret*sum;
                }
                sum=sum*sum;
                b>>=1;
            }
//          ret.prLL();
            return ret;
        }

        void writeV(LL x,LL y,LL val) {
            v[x][y]=val;
        }

        LL callV(LL x,LL y){
            return v[x][y];
        }
};

    juzhen a(1,3),b(3,3);
int main(void){
    cin>>n>>m;
    a.writeV(1,1,1);
    a.writeV(1,2,1);
    a.writeV(1,3,2);

    b.writeV(1,1,1);
    b.writeV(1,2,1);
    b.writeV(1,3,1);
    b.writeV(2,1,1);
    b.writeV(2,2,0);
    b.writeV(2,3,1);
    b.writeV(3,1,0);
    b.writeV(3,2,0);
    b.writeV(3,3,1);

    juzhen c=a*(b^(n-2));
    cout<<c.callV(1,3)<<endl;

    return 0;
}

偶数个3

描述

编程求出所有的 n 位数中,有多少个数中有偶数个数字 3。

輸入

一行一个正整数 n,0<n<1000。

輸出

一行一个正整数,表示 n 位数中有多少个数有偶数个 3。最后答案为12345取模

輸入範例 1

2

輸出範例 1

73

思路

好难一道题!

看着大佬打完表找出规律AC了,我的眼眶也湿润了。

然后我也dfs打了个表

然而找不到半分规律

最后看了大佬的规律和题解。。。

发现这规律可能我这辈子都找不出来。。

可能这就是我和大佬的差距吧

这里的大佬指的是(wyx大佬)[https://wuyanxi.top]和(perisino大佬)[https://cnblogs.com/perisino]

忽略上面这部分

主要解法就是用f[i][0]表示i位数中有偶数个3的数量

? 用f[i][1]表示i位数中有奇数个3的数量

若一个(i-1)位数中有偶数个3,要使i位数有偶数个3

则可以在其后追加1,2,4,5,6,7,8,9,0

若有奇数个,则可追加一个3

可得

f[i][0]=f[i-1][0]*9+f[i-1][1];
f[i][1]=f[i-1][1]*9+f[i-1][0];

回文拆分

描述

对一个正整数K,求出K的所有拆分,并统计输出其中回文数列的个数。 所谓回文数列是指该数列中的所有数字,从左向右或从右向左看都相同。 例如:

K=4时,有如下的拆分:

4=1+1+1+1 (回文数列1)

=1+1+2

=1+2+1 (回文数列2)

=2+1+1

=2+2 (回文数列3)

=1+3

=3+1

回文数列共有3个

輸入

一行,一个正整数K,1<=K<=26

輸出

一个正整数,表示回文数列个数

輸入範例 1

4

輸出範例 1

3

思路

有的题在递推的contest里,但它实际上是道递归题

? ——鲁迅

写了个dfs想看看能过几个点,没想到AC了。

#include<cstdio>
#include<iostream>
using namespace std;

int f[30];

void dfs(int step,int tot);
int k,cnt;

int main(){
    cin>>k;
    dfs(1,0);
    cout<<cnt<<endl;
    return 0;
}

void dfs(int step,int tot){
    if(tot>k)return;
    if(tot==k){
        bool flag=1;
        for(int i=1;i<=(step-1)/2;++i){
            if(f[i]!=f[step-i]){
                flag=0;
                break;
            }
        }
        if(flag){
            ++cnt;
//          for(int i=1;i<step;++i){
//              cout<<f[i]<<' ';
//          }
//          cout<<'\n';
        }
    }
    for(int i=1;i<=k-tot&&i<k;++i){
        f[step]=i;
        dfs(step+1,tot+i);
    }
}

看到自己是400+ms,而其他人都是2,3ms,感觉很尴尬

就打了个表

for(int i=1;i<=26;++i){
    cnt=0;
    memset(f,0,sizeof(f));
    k=i;
    dfs(1,0);
    cout<<i<<' '<<cnt<<endl;
}

得到

这不是\((2^n-1)\)吗。。。

所以又AC一遍

#include<cstdio>
#include<iostream>
using namespace std;

int qkpow(int x,int y);

int main(void){
    int k;
    cin>>k;
    cout<<qkpow(2,k/2)-1<<endl;
    return 0;
}

int qkpow(int x,int y){
    if(y==0){
        return 1;
    }
    int ret=1,sum=x;
    while(y){
        if(y&1){
            ret*=sum;
        }
        sum*=sum;
        y>>=1;
    }
    return ret;
}

~~

然后去找了找正解

每个数必须被分成3部分。

以4为例:4=1+2+1,我们发现中间的数字只能是偶数,即2和0,为2的时候有1种数列,0的时候有2种数列。

再来研究一下6,当为4的时候有1种,为2的时候有2种,为0的时候有4种。

最后看一下5,情况和4非常的相似,只是中间的数字只能是奇数。

就是令f[i]为i的拆分可能的情况,如果i为奇数,f[i]=f[i-1],i为偶数,f[i]=f[i-1]*2

哇,%%%

原文地址:https://www.cnblogs.com/buringstraw/p/10392846.html

时间: 2024-12-19 00:56:25

一堆递推题的相关文章

hdu 1267 下沙的沙子有几粒?(二维递推题)

题意:就是给你m个H和n个D,然后从左开始数H的累积个数总是不比D的累计数少的排列有多少种举一个测试案例吧:3个H和1个D总共有3种排列,依次是:H D H H,H H D H,H H  H D三种排列,亲~意思应该懂了吧?!呵呵... 思路:递推公式为:a[m][n]=a[m-1][n]+a[m][n-1];然后当n=0的时候无论m取何值都是1,递推公式怎么推来的呢?我现在说下我的思路吧!假设3个H和2个D是由2个H和2个D还有3个H一个D推来的,2个H和2个D总共有H D H D,H H D

一道简单的递推题(快速幂+矩阵乘法优化+滚动数组)

问题 F: 一道简单的递推题 时间限制: 1 Sec  内存限制: 128 MB提交: 546  解决: 48[提交][状态][讨论版] 题目描述 存在如下递推式: F(n+1)=A1*F(n)+A2*F(n-1)+...+An*F(1) 求第K项的值对1000000007取模的结果 输入 单组测试数据 第一行输入两个整数 n , k (1<=n<=100,n<k<=10000000000) 第二行输入 n 个整数 F(1)   F(2)   ...   F(n) 第三行输入 n

【DP】一道递推题。。。

Bob想要构造一张由n个节点构成的图.构造的过程由两步组成:首先Bob会取出n个孤立的点,并把它们从1到n编号,然后对每个节点用一种颜色染色,Bob一共可以使用K种不同的颜色.接下来Bob会在这张图中加入一些有向边,对于每一个编号范围在[2,n]的节点i,Bob有可能选择一个节点j满足j < i并且节点i与j的颜色不同,然后加入一条从i到j的有向边,对于节点i,Bob也有可能不加任何的有向边.现在你需要回答Bob有可能构造出多少种不同的图. 输入: 第一行包含三个整数n,K. 输出: 输出一个整

折线分割平面(图形递推题)

hdu 折线分割平面 (2050) Problem Description 我们看到过很多直线分割平面的题目,今天的这个题目稍微有些变化,我们要求的是n条折线分割平面的最大数目.比如,一条折线可以将平面分成两部分,两条折线最多可以将平面分成7部分,具体如下所示. Input 输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(n大于0且n<=10000),表示折线的数量. Output 对于每个测试实例,请输出平面的最大分割数,每个实例的输出占一行. Samp

hdu2073递推题

无限的路 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7792 Accepted Submission(s): 4027 Problem Description 甜甜从小就喜欢画图画,最近他买了一支智能画笔,由于刚刚接触,所以甜甜只会用它来画直线,于是他就在平面直角坐标系中画出如下的图形: 甜甜的好朋友蜜蜜发现上面的图还是有点规则的,于是他

hdu2047.java递推题

阿牛的EOF牛肉串 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 29530    Accepted Submission(s): 13853 Problem Description 今年的ACM暑期集训队一共有18人,分为6支队伍.其中有一个叫做EOF的队伍,由04级的阿牛.XC以及05级的COY组成.在共同的集训生活中,大家建立了深

hdu 1267 递推

下沙的沙子有几粒? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4326    Accepted Submission(s): 2268 Problem Description 2005年11月份,我们学校参加了ACM/ICPC 亚洲赛区成都站的比赛,在这里,我们获得了历史性的突破,尽管只是一枚铜牌,但获奖那一刻的激动,也许将永远铭刻

hdu 1267 下沙的沙子有几粒? (递推)

下沙的沙子有几粒? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 2584    Accepted Submission(s): 1346 Problem Description 2005年11月份,我们学校参加了ACM/ICPC 亚洲赛区成都站的比赛,在这里,我们获得了历史性的突破,尽管只是一枚铜牌,但获奖那一刻的激动,也许将永远铭刻

递推DP HDOJ 5459 Jesus Is Here

题目传送门 题意:简单来说就是sn = sn-1 + sn-2递推而来,求其中所有c字符的:∑i<j:sn[i..i+2]=sn[j..j+2]=‘‘cff"(j−i) mod 530600414 分析:一开始觉得很难下手,类似于斐波那契数列,最后的数字会很大,不能正常求解.想到试试打表找规律,结果并没有找到什么规律...最后也没想出什么来.赛后才恍然大悟,这是递推题,拿来别人的思路: 串长度len,串中字符c的个数num,串中所有字符c的位置之和sum,串中所有字符c之间的距离之和ans