动态规划小题整理2(最佳加法表达式,Help Jimmy(POJ1661),滑雪(OpenJudge百练1088))

目录

一、最佳加法表达式

二、Help Jimmy(POJ1661)

三、滑雪(OpenJudge百练1088)


一、最佳加法表达式

题意:

有一个由1..9组成的数字串.问如果将m个加 号插入到这个数字串中,在各种可能形成的 表达式中,值最小的那个表达式的值是多少。
 样例输入:

5 3

1 2 3 4 5

输出 : 

24 

分析: 

假定数字串长度是n,添完加号后,表达式的最后 一个加号添加在第 i 个数字后面,那么整个表达 式的最小值,就等于在前 i 个数字中插入 m – 1 个加号所能形成的最小值,加上第 i + 1到第 n 个数字所组成的数的值(i从1开始算)。


设V(m,n)表示在n个数字中插入m个加号所能形成的表达式最小值,那么:

if    (m = 0) //没有加号

       V(m,n) = n个数字构成的整数

else if (n-1<m)  //加号的个数大于数字空位数

       V(m,n) = ∞

else

V(m,n) = Min{  V(m-1,i) + Num(i+1,n) } ( i = m … n-1)
Num(i,j)表示从第i个数字到第j个数字所组成的数。数字编号从1开始算。此操 作复杂度是O(j-i+1),可以预处理后存起来。

总时间复杂度:O(mn2) 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1005;
int a[N];//a[N]里面是存数字串
int num[N][N];//num[i][j]表示数字串a[N]的第i位到第j位之间的数字串表示的数组
int dp[N][N];//dp[i][j]在i个数字中插入j个加号所能形成的表达式最小值
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        for(int i=1;i<=n;i++)
            cin>>a[i];
        for(int i=1;i<=n;i++)//预处理,计算i到j数字组成的数字
        {
            num[i][i]=a[i];
            for(int j=i+1;j<=n;j++)
                num[i][j]=num[i][j-1]*10+a[j];
        }
        memset(dp,INF,sizeof(dp));//当n-1<m时,即插入的加号比数字空位多,设置为无穷大
        for(int i=1;i<=n;i++)
            dp[0][i]=num[1][i];//没有加号时
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                for(int k=i;k<=j;k++)
                {
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+num[k+1][j]);
                }
        cout<<dp[m][n]<<endl;
    }

    return 0;
}

二、Help Jimmy(POJ1661)

Description

"Help Jimmy" 是在下图所示的场景上完成的游戏。 

 

场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。 

Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。 

设计一个程序,计算Jimmy到底地面时可能的最早时间。 

Input

第一行是测试数据的组数t(0 <= t <= 20)。每组测试数据的第一行是四个整数N,X,Y,MAX,用空格分隔。N是平台的数目(不包括地面),X和Y是Jimmy开始下落的位置的横竖坐标,MAX是一次下落的最大高度。接下来的N行每行描述一个平台,包括三个整数,X1[i],X2[i]和H[i]。H[i]表示平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1 <= N <= 1000,-20000 <= X, X1[i], X2[i] <= 20000,0 < H[i] < Y <= 20000(i = 1..N)。所有坐标的单位都是米。 

Jimmy的大小和平台的厚度均忽略不计。如果Jimmy恰好落在某个平台的边缘,被视为落在平台上。所有的平台均不重叠或相连。测试数据保证问题一定有解。 

Output

对输入的每组测试数据,输出一个整数,Jimmy到底地面时可能的最早时间。

Sample Input

1
3 8 17 20
0 10 8
0 10 13
4 14 3

Sample Output

23 

 分析:

整个问题就被分解成两个子问题,即Jimmy所在位置下方第一块板左 端为起点到地面的最短时间,和右端为起点到地面的最短时间。 这两个子问题在形式上和原问题是完全一致的。 

不妨认为Jimmy开始的位置是一个编号为0,长度为0的板子, 假设LeftMinTime(k)表示从k号板子左端到地面的最短时间, RightMinTime(k)表示从k号板子右端到地面的最短时间,那么, 求板子k左端点到地面的最短时间的方法如下:

if ( 板子k左端正下方没有别的板子) {     

      if( 板子k的高度 h(k) 大于Max)

            LeftMinTime(k) = ∞;

      else 

             LeftMinTime(k) = h(k);

}

else if( 板子k左端正下方有别的板子且两板子之间的距离不超过Max) //做的时候没想到WA了半天

          LeftMinTime(k) = h(k)-h(m) + Min( LeftMinTime(m) + Lx(k)-Lx(m),  RightMinTime(m) + Rx(m)-Lx(k));

}

dp[i][0]表示以i号平台左边为起点到地面的最短时间,dp[i][1]]表示以i号平台右边为起点到地面的最短时间

ps:如果下面这组数据过了,就基本AC了。

2

3 8 7 2
6 14 6
4 10 4
5 14 2

1 6 10 20
2 3 5

answer:
17 10

 代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1010
using namespace std;
const int INF=0x3f3f3f3f;
//dp[i][0]表示以i号平台左边为起点到地面的最短时间,dp[i][1]]表示以i号平台右边为起点到地面的最短时间
int dp[maxn][2];
int N,X,Y,Max;
struct node
{
    int lx,ly,h;//lx表示板子左端,ly表示板子右端
}p[maxn];
struct rule
{
    bool operator()(const node &a,const node &b)
    {
        return a.h>b.h;//从高到底排
    }
};
int judge1(int k)//判断左边有没有板子
{
    int i;
    for(i=k+1;i<=N;i++)
    {   //k下面有板子且板子之间的高度相差不超过Max,一开始没注意这个条件WA了
        if(p[k].lx>=p[i].lx&&p[k].lx<=p[i].ly&&(p[k].h-p[i].h<=Max))
        {
            return i;//返回板子的编号
        }
    }
    return 0;
}
int judge2(int k)//判断右边有没有板子
{
    int i;
    for(i=k+1;i<=N;i++)
    {
        if(p[k].ly<=p[i].ly&&p[k].ly>=p[i].lx&&(p[k].h-p[i].h<=Max))
            return i;
    }
    return 0;
}
void lefttime(int k)
{
    int m=judge1(k);
    if(m==0)//没有板子
    {
        if(p[k].h>Max)
            dp[k][0]=INF;
        else
            dp[k][0]=p[k].h;
    }
    else//有板子
    {   //用递推方程推出dp[k][0]
        dp[k][0]=p[k].h-p[m].h+min(dp[m][0]+p[k].lx-p[m].lx,dp[m][1]+p[m].ly-p[k].lx);
    }
}
void righttime(int k)
{
    int m=judge2(k);
    if(m==0)
    {
        if(p[k].h>Max)
            dp[k][1]=INF;
        else
            dp[k][1]=p[k].h;
    }
    else
    {
        dp[k][1]=p[k].h-p[m].h+min(dp[m][0]+p[k].ly-p[m].lx,dp[m][1]+p[m].ly-p[k].ly);
    }
}
int shorttime()
//此函数的作用就是办理所有板子的编号,求出dp数组中的每一项,最后递推可求处我们想要的解即min(dp[0][0],dp[0][1]);
{
    for(int i=N;i>=0;i--)
    {
        lefttime(i);
        righttime(i);
    }
    int ans=min(dp[0][0],dp[0][1]);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>N>>X>>Y>>Max;
        p[0].lx=X;p[0].ly=X;p[0].h=Y;
        for(int i=1;i<=N;i++)
        {
            cin>>p[i].lx>>p[i].ly>>p[i].h;
        }
        sort(p,p+N+1,rule());
        shorttime();
        cout<<shorttime()<<endl;
//        for(int i=0;i<=N;i++)
//            cout<<"dp-"<<i<<",0="<<dp[i][0]<<" dp-"<<i<<",1="<<dp[i][1]<<endl;
    }
    return 0;
}

(这个代码改了半天才改出来,AC的时候是真的爽哈哈哈!)

三、滑雪(OpenJudge百练1088)

题目链接:http://bailian.openjudge.cn/practice/1088/ 

描述

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子

 1  2  3  4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9


一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。

输入

输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。

输出

输出最长区域的长度。

样例输入

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

样例输出 

25 

思路:

 

设置一个二位数组DP[i][j]:表示从点(i,j)下落的最大长度。

初始状态: 每个dp[i][j]都是1

递推状态: dp[i][j] 等于它周围四个点比a[i][j]低的,并且最大的那个点的dp[][]值,再加一。

dp[ i ][ j ] = max (dp[ i ][ j ],solve( i , j )+1);其中solve函数的作用是求出周围最大的dp值。

时间复杂度为O(n2)。

其中在递推求解的for循环中,思路上应该是先从高度低的求,求到高度最高的。所以将所有点的高度从小到大排序。但为了不改变点的位置信息,所以用定义了一个struct结构体,目的就是为了是排序过后能够保存点的位置坐标。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 10010
using namespace std;
struct node
{
    int x,y,high;
}b[maxn];
struct rule
{
    bool operator()(const node & s1,const node & s2){
        return  s1.high<s2.high;
    }
};

int a[110][100];
int dp[110][110];
int r,c;
int solve(int x,int y)
{
    int ans=0;
    /*在点(x,y)的周围找最大的dp值。*/
    if(x-1>=0&&a[x-1][y]<a[x][y])
    {
        if(ans<dp[x-1][y])
            ans=dp[x-1][y];
    }
    if(x+1<r&&a[x+1][y]<a[x][y])
    {
        if(ans<dp[x+1][y])
            ans=dp[x+1][y];
    }
    if(y-1>=0&&a[x][y-1]<a[x][y])
    {
        if(ans<dp[x][y-1])
            ans=dp[x][y-1];
    }
    if(y+1<c&&a[x][y+1]<a[x][y])
    {
        if(ans<dp[x][y+1])
            ans=dp[x][y+1];
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
//    freopen("in.txt","r",stdin);
    cin>>r>>c;
    int k=0;
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
        {
            cin>>a[i][j];
            b[k].x=i;//保存点的坐标
            b[k].y=j;
            b[k].high=a[i][j];
            k++;
            dp[i][j]=1;//初始状态都为1
        }
    sort(b,b+r*c,rule());//让高度从小到大排序
    for(int i=0;i<r*c;i++)
    {
        dp[b[i].x][b[i].y]=max(dp[b[i].x][b[i].y],solve(b[i].x,b[i].y)+1);//递推方程,记得要加一
    }
    int ans=0;
    for(int i=0;i<r;i++)
    {
        for(int j=0;j<c;j++)
        {
            if(ans<dp[i][j])
                ans=dp[i][j];
//            cout<<dp[i][j]<<" ";
        }
//        cout<<endl;
    }
    cout<<ans<<endl;
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值