動態規劃小題整理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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章