7.19 暑期集训——动态规划篇(一)

3 篇文章 0 订阅
2 篇文章 0 订阅

一共十一题,按照从易到难的顺序给出,除了最后一题的思路要好好找一下,其它的一些细节注意一下就好啦,比较简单的题会有些省略
其中 放在最下面的三题
最大报销额 ****
FATE *****
City Game **********
技巧和想法 可以多回顾一下

The Triangle

题目描述:
给出一个三角形,如
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
找从顶到底 和最大的路径,输出和的最大值

思路:基础DP, 递推一下就好啦

命运

思路同上

饭卡

题目描述:若校园卡上的剩余金额大于或等于5元,即使购买后卡上余额为负,也可以购买商品,否则即使金额足够,也无法购买。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格w以及卡上的余额m,问最少可使卡上的余额为多少。

思路: 把价格最大的菜品先放到一边,从另外n-1种菜中选择,找出不超过m-5的最大消费额,再减去最大的菜品价格即为解~

PS:余额小于5时不能再操作啦~

Max Sum

题目描述: 给你一串数组序列:a[1],a[2],a[3]……a[n],求出最大连续子序列的和,输出最大和 及 最大和子序列的首尾元素值~

思路: 定义状态 d[i] 为以元素a[i]结尾的最大序列和,则d[i]只有两种取值
1、当前i-1的最大序列和小于等于0时,以a[i]元素为序列起始元素 d[i] = 0 + a[i];
2、否则a[i]的元素加到d[i-1] 上, d[i] = d[i-1] + a[i];
同时用s[i]数组记录以a[i]为结尾的最大子序列的起始位置。

最大连续子序列

题目描述: 给定K个整数的序列{ N1, N2, …, NK },其任意连续子序列可表示为{ Ni, Ni+1, …,
Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,
例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和
为20。
要求编写程序得到最大和,且增加一个要求,即还需要输出该子序列的第一个和最后一个元素。

输出 1、对每个测试用例,在1行里输出最大和、最大连续子序列的第一个和最后一个元素,中间用空格分隔;
2、如果最大连续子序列不唯一,则输出序号i和j最小的那个(如输入样例的第2、3组);
3、若所有K个元素都是负数,则定义其最大和为0,输出整个序列的首尾元素。

思路: 同上面一题思路大致相同,输出的时候需要注意三点 orz
1、如果比较下来的最大和sum<0 的话,说明所以k个元素都是负数,需要输出 0;
2、比较最后的sum时,只有d[i]>sum的时候需要更新,否则 j 不是最小的
3、记录序列开始的数组s[i],只有在d[i-1]<0的时候需要更新,d[i-1]==0 的时候若更新则起始点 i 不是最小的。

Common Subsequence:

题目描述: 给你两个字符序列 X =(x1, x2, …, xm)和 Z = (z1, z2, …, zk),找出最长的公共子序列包含的字母个数。
如字符串 a,b,c,f,b,c 和 a,b,f,c,a,b 的最长公共子序列为 a,b,c,b 输出4

思路: 定义状态d[i][j]为 X 取到第i个元素,Z取到第j个元素时 最长公共子序列的字符数
则状态转移为 x[i]==z[j] 时, d[i][j]=d[i-1][j-1]+1; (理解)
否则 d[i][j]=max(d[i-1][j],d[i][j-1]);

Monkey and Banana

思路
类似紫书P262 DAG模型中的嵌套矩形问题,不过每次加1 改为 加上长方体的高。

将一个长方体 看做 三个h不同,x小于y且不可颠倒的长方体,按照x从小到大排序,求最长上升子序列的和
d[i] = max(d[j]) + rect[i].h; 其中 j < i && rect[j].x < rect[i].x , rect[j].y < rect[i].y

Super Jumping! Jumping! Jumping!

即找最大上升子序列的和
d[i] = max(d[j]) + a[i]; 其中 a[j] < a[i];

最大报销额

题目描述: 现有一笔经费可以报销一定额度的发票。允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元。现请你编写程序,在给出的一堆发票中找出可以报销的、不超过给定额度的最大报销额。

输出:对每个测试用例输出1行,即可以报销的最大数额,精确到小数点后2位。

思路:
1.*** 输入的金额都是两位小数,可以*100使其变成整数
2. 注意选择可报销的发票的时候,相同类型的金额要加在一起,且是总和不超过600 (因为没有考虑到这个WA到无可救药 T_T)

FATE

最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能升掉这最后一级吗?

Input

输入数据有多组,对于每组数据第一行输入n,m,k,s(0 < n,m,k,s < 100)四个正整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下来输入k行数据。每行数据输入两个正整数a,b(0 < a,b < 20);分别表示杀掉一只这种怪xhd会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)

Output

输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。

Sample Input

10 10 1 10
1 1
10 10 1 9
1 1
9 10 2 10
1 1
2 2

Sample Output

0
-1
1

思路
定义状态 d[s][m] 表示 最多杀s只怪,还剩m点忍耐值时能获得的最大经验值
则状态转移时的源代码为

for(int i=0;i<=s;i++){       //杀怪数循环
    for(int j=0;j<=m;j++){     //忍耐值循环
       d[i][j]=(i==0)? 0:d[i-1][j];
       if(i)
              for(int t=0;t<k;t++){//从第0到第s个怪,循环,每个怪只能杀一次
               if(j>=mon[t].to)
               d[i][j]=max(d[i][j],d[i-1][j-mon[t].to]+mon[t].ex);
                }
            }
        }

注意
当剩余的忍耐值小于怪的忍耐值时,尽管还有剩余的忍耐值,这个怪也是不能杀的
因为这一点理解错我来回折腾了不知不少回…… QAQ……

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int d[110][110];
int n,m,k,s;
struct monster{
    int ex,to;
    monster(int a=0,int b=0):ex(a),to(b) {};
};
monster mon[110];

int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d%d%d",&n,&m,&k,&s)){
        for(int i=0;i<110;i++) mon[i]=monster();   //初始化和读入数据
        for(int i=0;i<k;i++){
            int a,b; scanf("%d%d",&a,&b);
            mon[i]=monster(a,b);
        }
        memset(d,0,sizeof(d));
        for(int i=0;i<=s;i++){           //DP
            for(int j=0;j<=m;j++){
                    d[i][j]=(i==0)? 0:d[i-1][j];
                if(i)
                    for(int t=0;t<k;t++){
                        if(j>=mon[t].to)
                            d[i][j]=max(d[i][j],d[i-1][j-mon[t].to]+mon[t].ex);
                }
            }
        }
        int ans=0;
        if(d[s][m]>=n){    //当可以升级时,要找所需的最小经验值
            int cnt=m;
            for(int i=0;i<=s;i++)
                for(int j=0;j<=m;j++){
                    if(d[i][j]>=n){
                        cnt=min(cnt,j);
                    }
                }
            ans=m-cnt;
        }
        else    ans=-1; //不可以升级则输出-1,忍耐值永不可为负

        printf("%d\n",ans);
    }
    return 0;
}

City Game

题目描述
告诉你一块n*m 的字符型矩阵,找出里面只由’F’构成的最大的矩形面积,每单位面积利润为3,输出最大利润

输入
第一行 T 为数据组数
第二行 n m 声明矩阵为 n行m列的
接下来 n 行的矩阵

输出
最大利润(即最大矩形面积*3)

思路
d[i] 表示 第i行此时的最大深度,l[i] 记录此深度的起点,r[i]记录终点,加以循环

**

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
int n,m;
int pic[1100][1100];
int h[1100],l[1100],r[1100];
int left,right;

int solve(){
    int res=0;
    for(int i=0;i<m;i++) {r[i]=m-1;l[i]=-1;}
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(pic[i][j]==1){
                ++h[j];
            }   else{
                h[j]=0;
            }
        }
        int left=-1;
        for(int j=0;j<m;j++){
            if(pic[i][j]==1)
                l[j]=max(left,l[j]);
            else
                left=j;
        }
        int right=m-1;
        for(int j=m-1;j>=0;j--){
            if(pic[i][j]==1){
                r[j]=min(r[j],right);
            }
            else right=j-1;
        }
        //for(int j=0;j<m;j++)
        //    printf("%d %d %d %d %d\n",i,j,h[j],l[j],r[j]);

        for(int j=0;j<m;j++){
            int area=h[j]*(r[j]-l[j]);
            res=max(area,res);
        }
    }

    return res;
}


int main(){
    //freopen("1.txt","r",stdin);
    int t; scanf("%d",&t);
    while(t--){
        memset(pic,0,sizeof(pic));
        memset(h,0,sizeof(h));
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++){
            string s; cin>>s;
            if(s[0]=='F') pic[i][j]=1;
            else pic[i][j]=0;
        }
        int res=solve();
        printf("%d\n",res*3);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值