牛客:【2021秋季算法入门班第七章习题:动态规划1】部分题解:方块与收纳盒、舔狗舔到最后一无所有、可爱の星空、[NOIP1999]拦截导弹

题单链接:牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

 P1001 方块与收纳盒

传送门:1001-方块与收纳盒_2021秋季算法入门班第七章习题:动态规划1 (nowcoder.com)

题目描述

   现在有一个大小n*1的收纳盒,我们手里有无数个大小为1*1和2*1的小方块,我们需要用这些方块填满收纳盒,请问我们有多少种不同的方法填满这个收纳盒

输入描述:

第一行是样例数T
第2到2+T-1行每行有一个整数n(n<=80),描述每个样例中的n。

输出描述:

对于每个样例输出对应的方法数

示例1

输入

3
1
2
4

输出

1
2
5

说明

n=4,有五种方法
1:1 1 1 1
2:2 1 1
3:1 2 1
4:1 1 2
5:2 2

简单的dp,也可以看成斐波那契数,代码如下:

#include<bits/stdc++.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int N=1005;
ll t,n,f[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--)
    {
        memset(f,0,sizeof(f));
        cin>>n;
        f[0]=0;
        f[1]=1;
        f[2]=2;
        for(int i=3;i<=n;i++)
        {
            f[i]=f[i-1]+f[i-2];
        }
        cout<<f[n]<<endl;
    }
    return 0;
}

 P1002 舔狗舔到最后一无所有

传送门:​​​​​​1002-舔狗舔到最后一无所有_2021秋季算法入门班第七章习题:动态规划1 (nowcoder.com)

题目描述

作为队伍的核心,forever97很受另外两个队友的尊敬。
Trote_w每天都要请forever97吃外卖,但很不幸的是宇宙中心forever97所在的学校周围只有3家forever97爱吃的外卖
如果Trote_w给forever97买了别家的外卖,forever97就会大喊“我不吃我不吃”。
但是forever97又不喜欢连续三天吃一种外卖
如果Trote_w哪天忘了这件事并且三天给他买了同一家外卖,那么forever97就会把Trote_w的头摁进手机屏幕里。
作为Trote_w的好朋友,你能告诉他连续请forever97吃n天饭,有多少不同的购买方法吗?

输入描述:

多组样例
第一行一个整数T(1<=T<=20)代表测试样例数
接下来t行每行一个整数n,代表Trote_w要请forever97吃n天饭(1<=n<=100000)

输出描述:

输出T个整数代表方案数,由于答案太大,你只需要输出mod 1e9+7 后的答案即可。

示例1

输入

2
3
500

输出

24
544984352

 思路:

 

 代码:

#include<bits/stdc++.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int N=100005;
const int mod=1e9+7;
ll t,n,f[3][N];//f[i][j]存的是前j天且第j天去第i家的总方案
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    f[0][0]=f[1][0]=f[2][0]=0;
    f[0][1]=f[1][1]=f[2][1]=1;//分别表示第1天去第0家、第1家、第2家的方案数
    f[0][2]=f[1][2]=f[2][2]=3;//分别表示第2天去第0家、第1家、第2家的方案数
    /*
    0 0      0  1       0  2
    0 1      1  1       1  2
    0 2      2  1       2  2
    */
    for(int i=3;i<=N;i++)
    {
        f[0][i]=(f[1][i-1]+f[2][i-1]+f[1][i-2]+f[2][i-2])%mod;
        f[1][i]=(f[0][i-1]+f[2][i-1]+f[0][i-2]+f[2][i-2])%mod;
        f[2][i]=(f[1][i-1]+f[0][i-1]+f[1][i-2]+f[0][i-2])%mod;
    }
    while(t--)
    {
        cin>>n;
        cout<<(f[0][n]%mod+f[1][n]%mod+f[2][n]%mod)%mod<<endl;
    }
    return 0;
}

 P1003 可爱の星空

题目链接:1003-可爱の星空_2021秋季算法入门班第七章习题:动态规划1 (nowcoder.com)

题目描述

“当你看向她时,有细碎星辰落入你的眼睛,真好。”——小可爱

在一个繁星闪烁的夜晚,卿念和清宇一起躺在郊外的草地上,仰望星空。

星语心愿,他们,想把这片星空的星星,连成一棵漂亮的树,将这美好的景色记录下来。

现在,天上共有n颗星星,编号分别为1,2.....n,一开始任何两个点之间都没有边连接。

之后,他们两个想在在(u,v)之间连无向边,需要付出|u联通块大小-v联通块大小|的代价。

他们两个想用最少的代价来使这n个点联通,所以他们想知道最小代价是多少。

(多组数据)

输入描述:

第一行一个正整数,表示数据组数T

接下来T行每行一个正整数,表示询问的n

输出描述:

T行,每行一个数表示答案

示例1

输入

1
5

输出

2

说明

1,2....5五个点,连边顺序为(1,2),(3,4),(1,5),(5,3),代价为0,0,1,1,总代价为2,是n=5的时候最优答案。

虽然(1,2),(2,3),(3,4),(4,5)也可以,但是代价为0,1,2,3,总代价为6,比2大。

备注:

对于20%的数据,T<=2,n<=10

对于40%的数据,T<=10,n<=1000

对于60%的数据,T<=100000,n<=100000

对于另外40%的数据,T=1,n<=1000000000000

 思路:

 由此可以看出来,代价最小就要两个连通块的差值最小,比如上图的要凑11,就用连通块大小分别为5、6来达到代价最小;在依次向下递归,凑5,就用连通块大小分别为2、3来达到代价最小;凑6,就用连通块大小分别为3、3来达到代价最小.....

代码:

#include<bits/stdc++.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdio.h>
#include<iomanip>
using namespace std;
typedef long long ll;//数据范围很大用long long存
const int N=100005;
const int mod=1e9+7;
ll fun(ll n)//递归求解
{
    if(n==1) return 0;//当n为0的时候,不需要合并,即代价为0
    if(n==2) return 0;//当n为2的时候,合并两个块为1的代价为|1-1|=0
    if(n&1)//n为奇数的情况
        return fun(n/2)+fun(n/2+1)+1;//奇数的情况,合并代价为1
    else//n为偶数的情况
        return fun(n/2)*2;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    ll t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        cout<<fun(n)<<endl;
    }
    return 0;
}

 P1004数字三角形

传送门:1004-数字三角形_2021秋季算法入门班第七章习题:动态规划1 (nowcoder.com)

题面:

 入门题了,不知道为什么放在动态规划里面。

代码: 

#include<bits/stdc++.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdio.h>
#include<iomanip>
using namespace std;
typedef long long ll;
const int N=100005;
const int mod=1e9+7;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    int temp=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            cout<<setw(4)<<temp++;//默认右对齐
            //cout<<std::left << setw(4) <<temp++;//左对齐写法
        }
        cout<<endl;
    }
    return 0;
}

 P1012 [NOIP1999]拦截导弹

 传送门:1012-[NOIP1999]拦截导弹_2021秋季算法入门班第七章习题:动态规划1 (nowcoder.com)

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入描述:

1行,若干个整数(个数≤100000)

输出描述:

2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

示例1

输入

389 207 155 300 299 170 158 65

输出

6
2

读入数据的时候用stringstream比较方便,stringstram用法传送门:stringstream的用法与例题(牛客“次佛锅”)_吴雨4的博客-CSDN博客_stringstream遇到空格

 本题思路:

模拟计算过程:从第一个数开始,找一个最长的递减子序列,即第一个拦截系统X,在案例中是{365,300,299,170,158,65},去掉这些数,序列中还剩下{207,155};在剩下的序列中再找一个最长的递减序列,即第二个拦截Y,是{207,155}。在Y中,至少有一个数a大于X中的某个数。否则a比X的所有数都小,应该在X中。所以从每个拦截系统中拿一个数能构成应该递增子序列,即拦截系统的数量等于这个递增子序列的长度。且最多能拦截多少导弹为最长递减序列的长度

 最长递增子序列传送门:动态规划【二】(线性dp)_吴雨4的博客-CSDN博客

大概思路:

代码:

#include<bits/stdc++.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdio.h>
#include<iomanip>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
ll a[N];
ll dpj[N],dpz[N];
string str;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    getline(cin,str);//由于读入是一行,getline存一行字符串
    stringstream s;//stringstream真的很好用
    s<<str;//把str给到stringstream变量s里面
    int n,tem=1;//每读一个整数n就存入a中
    while(s>>n) a[tem++]=n;//就有了长度为tem的整数数组a咯
    tem-=1;
    //cout<<tem<<endl;
    //for(int i=1;i<=tem;i++) cout<<a[i]<<"  ";

    //找最长递增子序列
    for(int i=1;i<=tem;i++)
    {
        dpz[i]=1;//只有a[i]一个数的时候dpz[i]长度就是1
        for(int j=1;j<i;j++)
            if(a[j]<a[i]) dpz[i]=max(dpz[i],dpz[j]+1);//转态转移方程
    }
    ll zeng=0;
    for(int i=1;i<=tem;i++) zeng=max(zeng,dpz[i]);

    //找最长递加减子序列
    for(int i=1;i<=tem;i++)
    {
        dpj[i]=1;//只有a[i]一个数的时候dpj[i]长度就是1
        for(int j=1;j<=i;j++)
            if(a[j]>a[i]) dpj[i]=max(dpj[i],dpj[j]+1);//转态转移方程
    }
    ll jian=0;
    for(int i=1;i<=tem;i++) jian=max(jian,dpj[i]);
    cout<<jian<<endl<<zeng<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值