week 13

文章介绍了五个编程问题,涉及构建特定模式的正方形、基于斐波那契数列的楼梯走法、德州扑克的牌型判断、订单编号的重新分配和任务的最优分配策略。每个问题都提出了具体输入输出格式和示例,以及解决思路和代码示例。
摘要由CSDN通过智能技术生成

特殊的正方形

输入n,输出nn列的由+和.组成的正方形,其中最外面一圈全是+,第二圈全是.,...,对于第i圈,如果i是奇数,那么全是+,否则全是.。

输入格式

一行,一个整数n

输出格式

n行,为满足题目要求的正方形。注意不要有行末空格。

样例输入
10
样例输出
++++++++++
+........+
+.++++++.+
+.+....+.+
+.+.++.+.+
+.+.++.+.+
+.+....+.+
+.++++++.+
+........+
++++++++++
数据范围

对于100%100%的数据,保证2≤n≤100。

这题是一道很常规的水题,根据题目的要求来看奇数圈为+,偶数圈为.

那么就很简单了,对于一个输入的圈数n,我们可以直接建立一个二维数组来储存这个图

第一圈绝对是+,那么下一圈就是.对于圈的定义我们可以定义第一圈为第1行加第1列加第n行加第n列

那么下一圈就是第2行加第2列加第n-1行加第n-1列,发现回合第一圈又部分重复,那我们就再加一个判断的部分,如果这个二维数组的这个位置没有被上一圈用过,我们在对它进行占位输出

include <bits/stdc++.h>
using namespace std;
int zfx[105][105];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){//i表示圈数
        if(i%2==1){//奇数圈,为+ 
            for(int j=i;j<=n;j++){
                if(zfx[i][j]!=2){
                    zfx[i][j]=1;//1表示+
                    zfx[j][i]=1;
                    zfx[n-i+1][j]=1;
                    zfx[j][n-i+1]=1; 
                }
            }
        } 
        else{
            for(int j=i;j<=n;j++){
                if(zfx[i][j]!=1){
                    zfx[i][j]=2;//2表示.
                    zfx[j][i]=2;
                    zfx[n-i+1][j]=2;
                    zfx[j][n-i+1]=2; 
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(zfx[i][j]==1) cout<<'+';
            else cout<<'.'; 
        }
        cout<<endl;
    }
    return 0;
}

走楼梯2

楼梯有 n 阶,上楼可以一步上一阶,也可以一步上二阶。

但你不能连续三步都走两阶,计算走到第n阶共有多少种不同的走法。

输入格式

一行,一个数字,表示n

输出格式

输出走楼梯的方式总数。

样例输入
6
样例输出
12
数据规模

对于100%100%的数据,保证n≤50


这题就是斐波那契数列的运用,最简单的dp的进阶版

唯一的难点在于你如何判断他什么时候连续跳了三次两步

我们可以用二维数组来储存,一维的数组可以储存斐波那契数列,那我们再加一个维度来储存它跳两步的次数剩下的表达式就是

f[i][j]=f[i+1][0]+f[i+2][j+1] j小于等于1

f[i][j]=f[i+1][0] j等于2的时候

按照这个递推式代码如下

对于n这个点位,我们得到的答案就是

a[n][0]+a[n][1]+a[n][2]

#include <iostream>
#define int long long
using namespace std;
int n;
int a[55][3];
signed main()
{
    cin>>n;
    a[0][0]=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<3;j++){
            if(j==2){
                a[i+1][0]+=a[i][j];
                break;
            }
            a[i+1][0]+=a[i][j];
            a[i+2][j+1]+=a[i][j];
        }
    }
    int ans=a[n][0]+a[n][1]+a[n][2];
    cout<<ans<<endl;
    return 0;
}

走路

有一条很长的数轴,一开始你在00的位置。接下来你要走n步,第i步你可以往右走ai或者bi。

n步之后,0m的每个位置,能不能走到?

输入格式

第一行,两个整数n,m

接下来n行,每行两个整数ai,bi。

输出格式

一行,一共m+1个数,每个数都是0或1表示能否走到,数字之间不用空格隔开。

输入样例

3 10
1 2
2 6
3 3

输出样例

00000011001
数据规模

对于所有数据,保证1≤n≤100,1≤m≤10^5,1≤ai,bi≤1000;

对于这题,我们就得判断这一步走完之后,会不会比给定的m还大

如果不会出界,就可以把这个地方标记下来待会输出

用一个二维数组来一边储存步数,一边储存步幅大小

#include <iostream>
using namespace std;
int a[105][2];
int f[105][100005];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i][0]>>a[i][1];
    } 
    f[0][0]=1;
    int k=0;
    for(int i=0;i<n;i++){//i表示步数 f[i][j]
        for(int j=0;j<=m;j++){//f表示能到达的地方 
            if(j+a[i+1][1]<=m&&f[i][j]) f[i+1][j+a[i+1][1]]=1;//没有超出边界,并且是上个点 
            if(j+a[i+1][0]<=m&&f[i][j]) f[i+1][j+a[i+1][0]]=1;
        }
    }
    for(int j=0;j<=m;j++){
        cout<<f[n][j];
    }
    return 0;
}

简单分数统计

N 个好朋友在codeforces上参加一场包含 M 个题目的比赛, 比赛期间codeforces网站一共有 k 次提交。

已知每个题目的分数,

但是由于他们只能查到在比赛期间codeforces总共的提交记录(其他用户提交的其他题目记录也包含在内, 即存在不属于该场比赛的题目),

所以想请你编写一个程序算出他们每个人的分数。

输入格式

第一行三个整数 N, M, K 分别表示好朋友的个数, 题目的个数, 和提交的总次数(其中0<N,M,K<=200)。

接下来 N行 第 i 行输入为第 i 个人的id,

接下来 M 行 第 j 行输入为第 j 个题目的名称和分数,

接下来 K 行 第 k 行输入为第 k 次提交的提交者id, 题目名称和结果("WA" 或 "AC", 如果"AC"代表通过这个题目, 提交者获得对应分数)。

注: 题目名称和id均为仅包含英文字母和数字的字符串, 题目分数为小于等于 1e6 的正整数. 每一行的多个输入之间用空格隔开。

所有输入的字符串长度 length满足 0<length≤500。

所有用户id和题目名称不存在重名, 用户AC了某个题之后之后不会再重复提交该题, 好朋友们只会提交属于比赛的题目。

输出格式

输出 N 行, 第 i 行输出第 i 个人的名字和对应分数 (名字和分数用空格隔开)。

样例输入
2 2 4
GabrielPessoa
beza
metebronca 100
geometry 200
beza metebronca AC
ffern numbertheory AC
GabrielPessoa geometry WA
beza geometry AC
样例输出
GabrielPessoa 0
beza 300
样例解释

beza 过了 metebronca和geometry 拿到 300 分。

GabrielPessos 没有过题, 所以是 0 分。

还有一些其他选手提交的其他题目忽略不计。

结构体记录数据一个个访问就可以解决

#include <iostream>
using namespace std;
struct student{
    string name1;
    int score=0;
};
struct quest{
    string tmu1;
    int sco;
};
struct sj{
    string name2;
    string tmu2;
    string zt;
};
student a[205];
quest b[205];
sj c[205];
int main()
{
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=0;i<n;i++){
        cin>>a[i].name1;
    } 
    for(int i=0;i<m;i++){
        cin>>b[i].tmu1>>b[i].sco;
    }
    for(int i=0;i<k;i++){
        cin>>c[i].name2>>c[i].tmu2>>c[i].zt;
    }
    for(int i=0;i<k;i++){//遍历提交记录 
        for(int j=0;j<n;j++){//遍历人 
            if(c[i].name2==a[j].name1){//名字对上了 
                for(int l=0;l<m;l++){//搜索题目 
                    if(c[i].tmu2==b[l].tmu1){//题目对上了 
                        if(c[i].zt=="AC") a[j].score+=b[l].sco; 
                    }
                }
            }
        }
    }
    for(int i=0;i<n;i++){
        cout<<a[i].name1<<' '<<a[i].score<<endl;
    }
    return 0;
}

Alice的德州扑克

德州扑克是目前世界上最流行的扑克游戏,全世界有众多相关的比赛,例如是 WSOP,WPT,EPT等,也让这款游戏的玩法变得层出不穷,丰富多变。 不要被简单的游戏规则而误导,复杂多变的比赛状况,让这款游戏在高水平的竞技中会变得非常复杂,这也让人们为德州扑克给出了这样一句评价 ”用一刻就能学会,但要用一生才能掌握” 。

现在我们并不在乎游戏规则是什么,因为 Alice 是一个德州扑克高手,他对于德州扑克的规则烂熟于心,不过他每次都记不得牌型的大小关系,他知道你是一个编程高手,所以他想让你帮他写一个程序:输入五张牌的大小和花色,输出这五张牌能组成的最大牌型.你能帮帮他吗?

为了降低你的编程难度,我们规定:

  1. 输入的牌都是来源于同一副扑克牌

  1. 输入的牌的点数都是非递减的

  1. 所有花色没有大小之分

下面给出各牌型,(从大到小)

  1. 皇家同花顺(ROYAL FLUSH):五张顺连的牌(点数连续单调递增),且最大的一张牌是A(Ace),并且五张牌的花色相同

  1. 同花顺(STRAIGHT FLUSH):五张顺连的牌(点数连续单调递增),不规定最大的一张牌是A(Ace),并且五张牌的花色相同

  1. 四条(FOUR OF A KIND):至少四张牌的点数相同

  1. 葫芦(FULL HOUSE):至少三张牌的点数相同,并且除此之外还有两张牌的点数相同

  1. 同花(FLUSH):五张牌的花色都相同

  1. 顺子(STRAIGHT):五张顺连的牌(点数连续单调递增),不要求五张牌的花色相同

  1. 特别注意:由于 Alice 是个谨慎的人,所以比 三条(THREE OF A KIND) (包括三条) 小的牌型 Alice 不在乎他们的大小关系,你只需要告诉 Alice 弃牌就行

输入格式

输入两行,每行五个数字,第一行的第 i个字符表示第 i 张扑克的点数,

第二行的第 i 个数字表示第 i 张扑克花色。(保证输入的牌的点数是非递减的,且所有输入均合法)

点数和对应输入的数字:

  • 2−10 对应 2 - 10

  • J(Jack) 对应 11

  • Q(Queen) 对应 12

  • K(King) 对应 13

  • A(Ace) 对应 14

花色和对应输入的数字:

  • 黑桃 (Spades) 对应 1

  • 方片 (Diamonds) 对应 2

  • 红桃 (Hearts) 对应 3

  • 梅花 (Clubs) 对应 4

输出格式

输出这五张牌能组成的最大牌型。

  • 如果最大是皇家同花顺输出 "ROYAL FLUSH"

  • 如果最大是同花顺输出 "STRAIGHT FLUSH"

  • 如果最大是四条输出 "FOUR OF A KIND"

  • 如果最大是葫芦输出 "FULL HOUSE"

  • 如果最大是同花输出 "FLUSH"

  • 如果最大是顺子输出 "STRAIGHT"

  • 如果最大的牌型小于等于三条输出"FOLD",劝 Alice 弃牌

  • 输出不包括引号

样例输入1
10 11 12 13 14
1 1 1 1 1
样例输出1
ROYAL FLUSH
样例输入2
10 11 12 13 14
1 2 1 3 4
样例输出2
STRAIGHT
样例输入3
6 6 6 7 7
1 2 3 1 3
样例输出3
FULL HOUSE
样例输入4
3 3 6 6 9
1 2 1 2 1
样例输出4
FOLD

这题也是很简单的一个判断类水题

#include <iostream>
using namespace std;
int pai[5];
int huase[5];
int main()
{
    int s=0;
    int ds[20];
    bool hs=true;
    bool th=true;
    for(int i=0;i<5;i++){
        cin>>pai[i];
    }
    for(int j=0;j<5;j++){
        cin>>huase[j];
    }
    for(int i=0;i<4;i++){
        if(pai[i+1]==pai[i]+1) s++;
        if(huase[i+1]==huase[i]) hs=true;//顺子 
        else hs=false;
    }
    if(s==4&&pai[4]==14&&hs){
        cout<<"ROYAL FLUSH";
        return 0;
        }//1
    else if(s==4&&hs&&pai[4]!=14){
        cout<<"STRAIGHT FLUSH";
        return 0;
        }//2
    for(int i=0;i<5;i++){
        ds[pai[i]]++;//对于每个点数的牌进行计数 
    }
    for(int i=2;i<=14;i++){
        if(ds[i]==4){
            cout<<"FOUR OF A KIND";
            return 0;
        }//3
        if(ds[i]==3||ds[i]==2){
            for(int j=2;j<=14;j++){
                if(ds[j]+ds[i]==5){
                    cout<<"FULL HOUSE";//4
                    return 0;
                }
            }
        }
    }
    for(int i=0;i<4;i++){
        if(huase[i]==huase[i+1]) th=true;
        else th=false;
    }
    if(th){
        cout<<"FLUSH";
        return 0;
        }//5
    if(s==4){
        cout<<"STRAIGHT";
        return 0;
    }//6
    cout<<"FOLD"; 
    return 0;
}

订单编号

小缘开了一家公司,生意很好,每天都会收到很多订单,自动交易系统会自动给这些订单生成没有重复的订单编号。但是有一天,系统出现了未知的错误,导致当天的订单编号可能有重复的,这可把小缘急坏了。你可以帮助小缘按照规则给这些订单重新编号吗?

按照时间先后顺序给出 N 个正整数作为原订单编号,你需要按照规则依次赋予这些订单新的编号,对于任意一个订单,要找到大于等于其原订单编号且未被使用过的(没有被之前的订单作为新的订单编号)的最小整数,作为它的新订单编号。

例如: 原订单编号依次为1 2 3 1,则新订单编号应该为1 2 3 4 (前3个订单的原订单编号都没有使用过,所以用其原订单编号即可,对于第四个订单,原订单编号为1,而1, 2, 3都已经被使用过,所以新订单编号为4)。

输入格式

第一行输入一个整数 N (1≤N≤5×1e5)

第二行输入 N个数 ai(1≤ai≤1e9) 作为原订单编号。

输出格式

输出一行,包含 N 个整数为新的订单编号。

样例输入1
6
2 3 4 1 1 1
样例输出1
2 3 4 1 5 6
样例输入2
3
1000000000 1000000000 1000000000
样例输出2
1000000000 1000000001 1000000002
样例输入3
6
4 5 1 2 1 1
样例输出3
4 5 1 2 3 6

这题是一个看这也不难的题目,按照我输入的这些数,如果有重复的,那我们就从这一些数里面的最小值开始遍历,直到找到一个最小的,没有被用过的订单编号

一开始的代码就如下

#include <iostream>
using namespace std;
int a[500005][3];//一个记录数字,一个记录出现次数 一个记录有没有被输出过 
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i][0];
a[i][1]+=1;//表示这个数字出现的次数 
a[i][2]+=1; 
}
int min=0x3f3f3f3f;
for(int j=1;j<=n;j++){//对于已经输出过一次的重复的故障处,我们要找到它在这些订单编号里面,大于最小编号且不重复的最小的数; 
if(a[j][0]<=min) min=a[j][0];//先找到最小值 
}
int j=min;
for(int i=1;i<=n;i++){
if(a[i][1]==1&&a[i][1]==a[i][2]){//就出现过一次 且没被输出过 
cout<<a[i][0]<<' ';
a[i][1]--;
continue;
}//下面的数字就是出现了两次或者两次以上的 
if(a[i][1]==a[i][2]){//这个数字没有被输出过 
cout<<a[i][0]<<' ';
a[i][1]-=1;
continue;
}
bool sc=false;
int jl=0;
for(j ;!sc;j++){//输出一次  
for(int k=1;k<=n;k++){//如果找到的这个数没有出现在a数组里面过 
if(a[k][0]==j){
sc=false;//如果找得到这个j,就不输出 外层循环继续 
    break;
}
jl++;
}
if(jl==n) sc=true;//表示循环完这个数组都没有找到j
jl=0; 
}
if(sc){
cout<<j-1<<' ';
a[i][1]-=1;
} 
}
    return 0;
}

但是它的数据太大了,a最大可以到达1e9那么普通的方法就实现不了了

我们就去看题解吧

他用的是set集合来定义区域的方法

set集合的好处在于不会有重复的,且自动排序

我们一开始可以放一个很大的区域进去,

再这个区域里面找到x后

把它分成两个部分,就是原来包括x的那个区域变成一个在x左边的区域,一个在x右边的区域

两个区域不包含x,顺便把x输出,接着在这两个区域里面再去找下一个值,

如果这个值不在一个个的子区域里面,也就是这个数重复了,

例如一开始的两个区域,第二个输入的数依旧是x,我们在区域里面找不到x了

我们就把大于x的那个区域的第一个数x+1输出(可以利用set里面的lower_bound来实现),再把那个区域变成x+1为左边界,右边界不变的区域

#include <bits/stdc++.h>
using namespace std;
int n;
set< pair<int,int> > a;
inline void insert(int l,int r){
    if(l>r) return;
    a.insert(make_pair(r,l));
} 
int main()
{
    a.insert(make_pair(2e9,1));
    cin>>n;
    for(int i=0;i<n;i++){
        int x;
        scanf("%d",&x);
        auto it=a.lower_bound(make_pair(x,0));//得到大于x的一个区间
        if(it->second<=x){//如果x包含在这个区间里面 也就是x没有重复出现过 
            printf("%d ",x);
            insert(it->second,x-1);
            insert(x+1,it->first);
            a.erase(it);//把这个区间分割成两部分,放入set里面,再把原来的那个大的集合删除 
        } 
        else{//如果这个区间不能包含x,也就是x重复出现过 
            printf("%d ",it->second);
            insert(it->second+1,it->first);
            a.erase(it);
        }
    }
    return 0;
}

记住,结构体不是基本数据类型,pair是基本数据类型

饿饿 饭饭

有n个同学正在排队打饭,第i个同学排在从前往后第i个位置。但是这天食堂内只有一个食堂阿姨,为了使同学们都能尽快的吃上饭,每一个同学在打完一份饭之后就会排在队伍的末尾先吃着打到的饭,我们知道第i个同学的饭量为ai,也就是说第i个同学要吃ai份饭才能吃饱,当一位同学吃饱后,他就会立刻离开食堂,不会排在队伍的末尾。食堂阿姨想知道,在打完k份饭之后,队伍的样子是怎样的,但是食堂阿姨数学不太好,想让你帮忙想想办法。

输入格式

第一行给出两个整数n,k。

第二行给出n个整数a1,a2,......an

输出格式

如果食堂阿姨打饭数少于k,请输出"-1"。

否则按照队伍顺序输出每一个同学的编号。

样例输入1
3 3
1 2 1
样例输出1
2
样例输入2
4 10
3 3 2 1
样例输出2
-1
样例输入3
7 10
1 3 3 1 2 3 1
样例输出3
6 2 3
数据规模

数据保证1≤n≤1e5, 0≤k≤1e14, 1≤ai≤1e9

这题第一眼一看,欸,这么简单,一个队列直接完成,如果他吃饱了就走出队列,没吃饱就继续排到队列后面,题目都没认真看完,直接代码写完

#include <bits/stdc++.h>
using namespace std;
struct student{
    int xh;
    int fanl;
};
student a[100005];
queue<int> b;
int main()
{
    int n,k;//n个同学,k份饭 
    scanf("%d%d",&n,&k);
    int ans=1;
    queue<int> b;
    for(int i=1;i<=n;i++){
        a[i].xh=i; 
        scanf("%d",&a[i].fanl);
        b.push(i);//同学学号排队 
    }
    for(int i=1;i<=k;i++){//打k次饭 
        if(b.empty()) ans=0; 
        int x=b.front();//弹出第一个学生的学号 
        b.pop();
        a[x].fanl-=1;
        if(a[x].fanl>0) b.push(x);
    }
    if(!ans){
        printf("%d",-1);
        return 0;
    }
    while(!b.empty()){
        printf("%d ",a[b.front()].xh);
        b.pop();
    }
    return 0;
}

提交发现全部超时...

认真看才发现k的值不太正常1e14不超时才怪

于是换个想法,

阿姨打饭,打k份,那么k份饭够打多少轮呢?

最后一轮的打饭完剩下的队列不就是我们要的结果

#include <bits/stdc++.h>
using namespace std;
long long k;
int n;
int a[100005];//用来记录每个人的不同饭量 
int c[100005];//用来记录最后的人的学号 
long long calc(int x){
    long long ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]<=x) ans+=a[i]; //打x轮的饭,如果这个同学需要的饭比x还小,就离开了队伍 
        else ans+=x;//需求比x大,就依然留在队伍里面,还差a[i]-x 份的饭 
    }
    return ans; 
}
int main()
{
    long long s=0;
    scanf("%d%ld",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        s+=a[i]; 
    }
    if(s<k){
        printf("%d",-1);//所有人要的饭都没阿姨给的饭多 
        return 0;
    }
    int l=0;
    int r=1e9;
    while(l+1<r){//为什么是l+1<r 我们要得到的是最后一轮打饭而不是把饭全部打完
        int m=l+(r-l)/2;//用二分找到打饭的最大轮数,得到最后一轮的饭 
        if(calc(m)<=k) l=m;//打了m轮的饭还是比k小,继续打饭 
        else r=m;
    }//打l轮的饭,还需要打的饭不够打一轮 
    k-=calc(l);//打了l轮的饭,阿姨离打完k份饭还差的量 
    int cnt=0;
    for(int i=1;i<=n;i++){
        if(a[i]>l){//这个同学还要吃饭 
            c[cnt++]=i;//把这个同学的学号记下来加创新创业学分/dog 
        }
    }//再模拟一下最后一轮打饭 
    for(int i=k;i<cnt;i++){//先看打完后的同学 
        printf("%d ",c[i]);
    }//再看打完k份的同学,如果他们还要吃的话还会排在后面 
    for(int i=0;i<k;i++){
        if(a[c[i]]>l+1) printf("%d ",c[i]);
    }
    return 0;
}

任务分配

你有n个任务,其中第i个任务,在si开始,ei时刻结束,如果做这个任务,你能获得wi的收益。

但是你在一个时刻只能做一个任务,问选择哪些任务,能让你的收益尽量大。

注意:你在上一个任务结束后马上开始下一个任务是可以的。

输入格式

第一行一个整数n。

接下来n行,每行三个整数si,ei,wi

输出格式

一个数,表示答案。

样例输入
3
1 3 100
2 4 199
3 5 100
样例输出
200
数据规模

对于所有数据,保证1≤n≤1e3,1≤si<ei≤1e3,1≤wi≤1e5

这题也是一眼就看出来了是dp,这算是贪心经典的那个例题和dp的结合吧

这边有两种思路,一种是一个任务一个任务这样去完成

另一个是一个时间点一个时间点去完成

#include <bits/stdc++.h>
using namespace std;
int n;
struct re{
int s,e,w;
};
re a[1005];
int f[1005];
bool cmp(re b,re c){
if(b.s==c.s) return b.e<c.e;
return b.s<c.s;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].s>>a[i].e>>a[i].w;
}
int maxn=0; 
sort(a,a+n,cmp);//正确 
for(int i=0;i<n;i++){
f[i]=a[i].w;
} 
for(int i=0;i<n;i++){//n个事件 从开始时间小到大排序好了 
for(int j=i;j<n;j++){
if(a[j].s>=a[i].e){//如果后面的时间的起点比前面的事件结束时间还后面就可以这两件事都参与 
f[j]=max(f[j],f[i]+a[j].w);//两种情况不做这件事,(第i件事)和做这件事 
}
}
}
for(int i=0;i<n;i++){
maxn=max(maxn,f[i]);
}
cout<<maxn<<endl;
    return 0;
}

路径计数

有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。

一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。

由于答案很大,输出对1e9+7取模的结果。

输入格式

第一行一个正整数n。

接下来n行,每行n个正整数,1表示可以通行,0表示不能通行。

输出格式

一个整数,表示答案。

样例输入
3
1 1 1
1 0 1
1 1 1
样例输出
2
数据规模

对于100%100%的数据,保证2≤n≤100,左上角右下角都是可以通行的。

这题要注意的就是dfs会超时...

而且0可能会出现在边界上,所以我们要先注意0的情况再去进行dp

这题也有两种写法思路一样

一种是在dp二层循环时一起把边界dp了

另一种是现在外面把边界初始化再进行dp

数据太大要取模,不是只有答案要取模,记得在dp过程里面取模。(过程没取模浪费了我两个小时de一个没有bug的代码)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n;
int ans;
int a[105][105];
int f[105][105];
signed main(){
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(a[i][j]==0) f[i][j]=0;
            else{
                if(i==0&&j==0) f[i][j]=1;
                else if(i==0) f[i][j]=f[i][j-1]%mod;
                else if(j==0) f[i][j]=f[i-1][j]%mod;
                else f[i][j]=f[i-1][j]+f[i][j-1]%mod;
            }
        }
    }
    ans=f[n-1][n-1]%mod;
    cout<<ans<<endl;
    return 0;
}

最大和上升子序列


给定一个长度为 n 的数组 a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n 并且 ap1<ap2<⋯<apm,使得ap1+ap2+⋯+apm最大。

输入格式

第一行一个数字 n。

接下来一行 n 个整数 a1,a2,…,an

输出格式

一个数,表示答案。

样例输入
6
3 7 4 2 6 8
样例输出
21
数据规模

所有数据保证 1≤n≤1000,1≤ai≤1e5

对于这题,不难让人想到最长上升子序列,思路是一样的只是这次我们得求他的和

一样的dp甚至代码结构可能都一样

我们只需要把计数变成求和就行

最后再在记录的数组里面求他的最大值

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int f[1005];
int maxn=0;
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        f[i]=a[i];
    }
    f[0]=a[0];//374268
    for(int i=1;i<n;i++){
        for(int j=i-1;j>=0;j--){
            if(a[i]>a[j]) f[i]=max(f[i],a[i]+f[j]);
        }
        if(f[i]>=maxn) maxn=f[i];
    }
    cout<<maxn;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值