1. 程式人生 > 實用技巧 >【講題】Galaxy OJ動態規劃基礎1——T1~T5

【講題】Galaxy OJ動態規劃基礎1——T1~T5

動態規劃基礎1

【傳送門】

T1 滑雪

難度:普及-, 做法:記憶化搜尋

首先for一遍起點,分別使用dfs搜尋,搜尋過程記錄答案,大大減少複雜度。

Code:

#include<bits/stdc++.h>
using namespace std;
int r,c,maxx=0,mx,my,dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};
int a[110][110],dp[110][110],ans;
int dfs(int x,int y)
{
    if(dp[x][y])	return dp[x][y];
    int r=0;
    for(int i=0;i<4;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(a[xx][yy]<a[x][y])
            r=max(r,dfs(xx,yy));
    }
    dp[x][y]=r+1;
    return ________;
}
int main()
{
    scanf("%d%d",&r,&c);
    memset(a,0x3f,sizeof(dp));
    for(int i=1;i<=r;i++)	
        for(int j=1;j<=c;j++)
        {
            scanf("%d",&a[i][j]);
            if(a[i][j]>maxx)
                mx=i,my=j,maxx=a[i][j];
        }
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
            ans=max(ans,dfs(i,j));
    cout<<ans;
}

T2 最長下降子序列

難度:普及-, 做法:動態規劃

\(dp_{i}\) 為區間 \([1,i]\) 中最長上升子序列的長度,不難得出狀態轉移方程為 \(dp_j=max(dp_j,\ dp_i+1)\ (1 \leq i \leq n,\ 1 \leq j < i,\ a_i<a_j)\)

Code:

#include<bits/stdc++.h>
using namespace std;
int a[5001],dp[5001],n,ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
            if(a[j]>a[i])
                dp[i]=max(dp[i],_____+1);
        ans=max(ans,dp[i]);
    }
    printf("%d",ans);
    return 0;
}

T3 簡單傳紙條

難度:入門+, 做法:你猜

很顯然這題的做法是 dfs DP 。設 \(dp_{i,j}\) 為走到座標 \((i,j)\) 的最小八卦值之和。那麼由於點 \((i,j)\) 只能由點 \((i-1,j)\)\((i,j-1)\) 到達,所以狀態轉移方程為 \(dp_{i,j}=min(dp_{i-1,j},dp_{i,j-1}+a_{i,j})\)

Code:

#include <bits/stdc++.h>
using namespace std;
int n,m,a[2200][2200],dp[2200][2200];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    memset(dp,0x3f,sizeof(dp));
    dp[1][1]=a[1][1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            dp[i][j]=min(dp[i][j],min(dp[i-1][j],dp[i][j-1])+_______);
    printf("%d",ans);
    return 0;
}

T4 數字三角形

難度:NOI/NOI+/CTSC, 做法:膜你退火 記憶化搜尋

這是IOI 1994原題。
做法為從頂端的點開始向下dfs,回朔時記錄下當前點的答案,以此避免 \(O(2^n)\) 的暴力搜尋,時間複雜度變為 \(O(n^2)\)

Code:

#include <bits/stdc++.h>
using namespace std;
int n,a[2200][2200],dp[2200][2200];
int dfs(int dep,int x)
{
    if(dep>n || x>dep)  return 0;
    if(dp[dep][x])    return dp[dep][x];
    dp[dep][x]=max(dp[dep][x],dfs(dep+1,x));
    dp[dep][x]=max(dp[dep][x],dfs(dep+1,x+1));
    return dp[dep][x]+=_________;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    printf("%d",dfs(1,1));
    return 0;
}

T5 遞迴函式

難度:普及-, 做法:(偽)暴力

直接將遞迴公式原原本本抄下來,加上記憶化即可。
注:需要一定的玄學優化,不然會不明不白地TLE

Code:

#include<bits/stdc++.h>
using namespace std;
int a,b,c,ans,dp[60][60][60];
int w(int x,int y,int z)
{
    if(x<=0 || y<=0 || z<=0)
        return dp[0][0][0]=1;
    if(dp[x][y][z])
        return dp[x][y][z];
    if(x>20 || y>20 || z>20)
        return dp[x][y][z]=w(20,20,20);
    if(x<y && y<z)
        return dp[x][y][z]=w(x,y,z-1)+w(x,y-1,z-1)-w(x,y-1,z);
    return dp[x][y][z]=w(x-1,y,z)+w(x-1,y-1,z)+w(x-1,y,z-1)-w(x-1,y-1,z-1);
}
int main()
{
    while(true)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(a==-1 && b==-1 && c==-1)
            break;
        if(a<=0 || b<=0 || c<=0)
            ans=1;
        else if(dp[a][b][c]==0)
            ans=w(a,b,c);
        else
            ans=dp[a][b][c];
        printf("w(%d, %d, %d) = %d\n",a,b,c,ans);
    }
    return 0;
}