zoukankan      html  css  js  c++  java
  • 数字三角形模型

    1015. 摘花生

    Hello Kitty想摘点花生送给她喜欢的米老鼠。
    她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
    地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。
    Hello Kitty只能向东或向南走,不能向西或向北走。
    问Hello Kitty最多能够摘到多少颗花生。

    输入格式
    第一行是一个整数T,代表一共有多少组数据。
    接下来是T组数据。
    每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。
    每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。
    输出格式
    对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。
    数据范围
    (1≤T≤100, 1≤R,C≤100, 0≤M≤1000)
    输入样例:
    2
    2 2
    1 1
    3 4
    2 3
    2 3 4
    1 6 5
    输出样例:
    8
    16

    思路:数字三角形模型最朴素的一种题:
    闫氏思考法:

    #include<iostream>
    #include<string.h>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<string>
    #include<set>
    #include<map>
    using namespace std;
    typedef pair<int,int> PII;
    typedef long long LL;
    const int N=110;
    int a[N][N],dp[N][N];
    int main(){
        int n,m,T;
        cin>>T;
        while(T--){
            cin>>n>>m;
            for(int i=1;i<=n;++i){
                for(int j=1;j<=m;++j)
                    cin>>a[i][j];
            }
            memset(dp,0,sizeof dp);
            for(int i=1;i<=n;++i){
                for(int j=1;j<=m;++j){
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
                }
            }
            cout<<dp[n][m]<<endl;
        }
        return 0;
    }
    

    1018. 最低通行费

    思路:同上一题,注意不要取到边界

    #include<iostream>
    #include<string.h>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<string>
    #include<set>
    #include<map>
    using namespace std;
    typedef pair<int,int> PII;
    typedef long long LL;
    const int N=110;
    int a[N][N],dp[N][N];
    int main(){
        int n,m,T;
        cin>>n;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j)
                cin>>a[i][j];
        }
        for(int i=2;i<=n;++i){
            dp[i][0]=0x3f3f3f3f;
            dp[0][i]=0x3f3f3f3f;
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j];
            }
        }
            cout<<dp[n][n]<<endl;
        return 0;
    }
    

    方格取数

    设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:

    某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点
    在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
    此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。
    输入格式
    第一行为一个整数N,表示 N×N 的方格图。
    接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
    一行“0 0 0”表示结束。
    输出格式
    输出一个整数,表示两条路径上取得的最大的和。
    数据范围
    N≤10
    输入样例:
    8
    2 3 13
    2 6 6
    3 5 7
    4 4 14
    5 2 21
    5 6 4
    6 3 15
    7 2 14
    0 0 0
    输出样例:
    67

    思路:
    可以判断两次路线是否走到同一格子来消除重复,只有在同一时间才有可能走到同一个格子即位置i+j相等,我们定义状态的一个值时k为时间,i1表示第一次的行坐标,i2表示第二次的行坐标,通过k-i1,k-i2就可以分别得到列坐标了。

    #include<iostream>
    #include<string.h>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<string>
    #include<set>
    #include<map>
    using namespace std;
    typedef pair<int,int> PII;
    typedef long long LL;
    const int N=15;
    int a[N][N],dp[N*2][N][N],n;
    int main(){
        cin>>n;
        int x,y,z;
        while(cin>>x>>y>>z&&(x||y||z))
            a[x][y]=z;
        for(int k=2;k<=2*n;++k){
            for(int i1=1;i1<=n;++i1){
                for(int i2=1;i2<=n;++i2){
                    int j1=k-i1,j2=k-i2;
                    if(j1<=0||j1>n||j2<=0||j2>n) continue;
                    int t=a[i1][j1];
                    if(i1!=i2){
                        t+=a[i2][j2];
                    }
                    dp[k][i1][i2]=max(dp[k-1][i1-1][i2]+t,dp[k][i1][i2]);
                    dp[k][i1][i2]=max(dp[k-1][i1][i2-1]+t,dp[k][i1][i2]);
                    dp[k][i1][i2]=max(dp[k-1][i1-1][i2-1]+t,dp[k][i1][i2]);
                    dp[k][i1][i2]=max(dp[k-1][i1][i2]+t,dp[k][i1][i2]);
                }
            }
        }
        cout<<dp[2*n][n][n]<<endl;
        return 0;
    }
    

    传纸条

    小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。
    一次素质拓展活动中,班上同学安排坐成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。
    幸运的是,他们可以通过传纸条来进行交流。
    纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。
    从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。 
    在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。
    班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙,反之亦然。 
    还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。
    小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。
    现在,请你帮助小渊和小轩找到这样的两条路径。
    输入格式
    第一行有2个用空格隔开的整数 m 和 n,表示学生矩阵有 m 行 n 列。
    接下来的 m 行是一个 m∗n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i 行 j 列的学生的好心程度,每行的 n 个整数之间用空格隔开。
    输出格式
    输出一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
    数据范围
    1≤n,m≤50
    输入样例:
    3 3
    0 3 9
    2 8 5
    5 7 0
    输出样例:
    34

    思路:
    从右下角到左上角的移动完全可以看作逆过程,那就变成了类似上一题两次移动取物不取重复,不过还要求不能走到同一格,即两次路线不能有交集。由于起点和终点相同,必定有一次移动在上,另一次移动在上,所以只要枚举第二次移动的行在第一次移动的行下面的情况即可。还需要注意在第一次移动只有在最后一步才能进入第N行,所以dp[k][n][n]可以留到最后特判。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=60;
    int a[N][N],dp[N+N][N][N];
    int main(){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                cin>>a[i][j];
        for(int k=2;k<=n+m;++k){
            for(int i1=1;i1<=n-1;++i1){
                for(int i2=i1+1;i2<=n;++i2){
                    int j1=k-i1,j2=k-i2;
                    if(j1<=0||j1>m||j2<=0||j2>m) continue;
                    int& v=dp[k][i1][i2];
                    int t1=a[i1][j1],t2=a[i2][j2];
                    v=max(dp[k-1][i1-1][i2]+t1+t2,v);
                    v=max(dp[k-1][i1][i2-1]+t1+t2,v);
                    v=max(dp[k-1][i1][i2]+t1+t2,v);
                    v=max(dp[k-1][i1-1][i2-1]+t1+t2,v);
                }
            }
        }
        int& v=dp[n+m][n][n];
        int k=n+m;
        v=max(dp[k-1][n-1][n],v);
        v=max(dp[k-1][n][n-1],v);
        v=max(dp[k-1][n][n],v);
        v=max(dp[k-1][n-1][n-1],v);
        cout<<dp[n+m][n][n]<<endl;
        return 0;
    }
    

    补充题:
    免费馅饼

    Problem Description
    都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标
    为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)
    Input
    输入数据有多组。每组数据的第一行为以正整数n(0<n<100000),表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(0<T<100000),表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。
    Output
    每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。
    提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。
    Sample Input
    6
    5 1
    4 1
    6 1
    7 2
    7 2
    8 3
    0
    Sample Output
    4

    思路:
    从某一顶点开始,每一个决策向左或向右,显然是一个数字三角形模型。然后考虑dp的顺序,由于时间小于100000,也就是有100000层数塔,每层11个位置。所以可以先枚举时间,再枚举位置。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010,M=15;
    typedef pair<int,int> PII;
    int dp[N][M],a[N][M];
    int main(){
        int n;
        while(scanf("%d",&n)!=EOF&&n){
            int maxt=0;
            memset(a,0,sizeof a);
            memset(dp,0,sizeof dp);
            for(int i=1,x,t;i<=n;++i){
                scanf("%d%d",&x,&t);
                a[t][x]++;
                maxt=max(maxt,t);
            }
            for(int i=maxt;i>=0;--i){
                dp[i][0]=max(dp[i+1][0],dp[i+1][1])+a[i][0];
                for(int j=1;j<=9;++j){
                    int &v=dp[i][j];
                    v=max(v,dp[i+1][j-1]+a[i][j]);
                    v=max(v,dp[i+1][j]+a[i][j]);
                    v=max(v,dp[i+1][j+1]+a[i][j]);
                }
                dp[i][10]=max(dp[i+1][10],dp[i+1][9])+a[i][10];
            }
            cout<<dp[0][5]<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    看动画学算法之:栈stack
    asp.net core使用identity+jwt保护你的webapi(三)——refresh token
    asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token
    与 Python 之父聊天:更快的 Python!
    Oracle使用SPM对含有绑定变量SQL做固定的方法
    Oracle Hint no_merge(merge)、no_unnest(unnest)的作用对象陷阱
    Oracle全表扫描导致的direct path read第一次慢第二次以后变快
    Oracle OEM13C添加主机监控遇到一些问题汇总
    还傻傻分不清楚equals和==的区别吗?看完就明白了
    从0到1实现一个简单计算器
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12534894.html
Copyright © 2011-2022 走看看