ACM公选课7/8 DP算法

DP算法

ACM公选课7/8 DP算法_第1张图片
动态规划法设计算法一般分成三个阶段:
(1)分段:将原问题分解为若干个相互重叠的子问题;
(2)分析:分析问题是否满足最优性原理,找出动态规划函数的递推式;
(3)求解:利用递推式自底向上计算,实现动态规划过程。

动态规划法利用问题的最优性原理,以自底向上的方式从子问题的最优解逐步构造出整个问题的最优解。

DP 就是要避免重复计算!

例题

穿过街道

#include 
using namespace std;
int f[100][100];
int getf(int x,int y)
{
    if (f[x][y]!=-1)
        return f[x][y];
    int result;
    if (x==0||y==0)
        result=1;
    else
        result=getf(x-1,y)+getf(x,y-1);
    f[x][y]=result;
    return result;
}
int main()
{
    int i,j,n;
    memset(f,-1,sizeof(f));
    while(cin>>n)
    {
        if (n==0)
            break;
        cout<<getf(n,n)<<endl;
    }
    return 0;
}

数字三角形

#include 
using namespace std;
int main()
{
    int data[102][102];
    int inp[102][102];
    int n;
    while(cin>>n)
    {
        memset(inp,0,sizeof(inp));
        memset(data,0,sizeof(data));
        for(int i=1; i<=n; i++)
            for(int j=1; j<=i; j++)
            {
                cin>>data[i][j];
                if (i==1)
                    inp[i][j]=data[i][j];
                else
                    inp[i][j]=max(inp[i-1][j],inp[i-1][j-1])+data[i][j];
            }
        int tmp=0;
        for(int k=1; k<=n; k++)
            if (tmp<=inp[n][k])
                tmp=inp[n][k];
        cout<<tmp<<endl;
    }
    return 0;
}

Function Run Fun

#include 
using namespace std;
long long data[21][21][21];
long long inf(int x,int y,int z)
{
    if (x<=0||y<=0||z<=0)
        return 1;
    if (x>20||y>20||z>20)
        return inf(20,20,20);
    if (x<y&&y<z)
    {
        if (data[x][y][z]==-1)
            data[x][y][z]=inf(x,y,z-1)+inf(x,y-1,z-1)-inf(x,y-1,z);
        return data[x][y][z];

    }
    else
    {
        if (data[x][y][z]==-1)
        {
            data[x][y][z]=inf(x-1,y,z)+inf(x-1,y-1,z)+inf(x-1,y,z-1)-inf(x-1,y-1,z-1);

        }
        return data[x][y][z];
    }
}
int main()
{
    int x,y,z;
    while(cin>>x>>y>>z)
    {
        if (x==-1&&y==-1&&z==-1)
            break;
        memset(data,-1,sizeof(data));
        cout<<"w("<<x<<", "<<y<<", "<<z<<")"<<" = "<<inf(x,y,z)<<endl;
    }
    return 0;
}

Recaman Sequence

#include 
using namespace std;
#define size 500000
int data[size+1];
bool inp[size*10];
int main()
{
    int n;
    memset(inp,false,sizeof(inp));
    data[0]=0;
    inp[0]=true;
    for(int i=1; i<=500000; i++)
    {
        if (data[i-1]-i>0&&inp[data[i-1]-i]==false)
            data[i]=data[i-1]-i;
        else
            data[i]=data[i-1]+i;
        inp[data[i]]=true;
    }
    while(cin>>n&&n!=-1)
    {
        cout<<data[n]<<endl;
    }
    return 0;
}

滑雪

#include 
using namespace std;
int data[101][101];
int inp[101][101];
int m,n;
int f(int i,int j)
{
    int max1,max2;
    int max_right=0,max_left=0,max_up=0,max_down=0;
    if (data[i][j]>0)
        return data[i][j];
    if (j+1<=n&&inp[i][j]>inp[i][j+1])
        max_right=f(i,j+1);//右边
    if (j-1>=1&&inp[i][j]>inp[i][j-1])
        max_left=f(i,j-1);//左边
    if (i+1<=m&&inp[i][j]>inp[i+1][j])
        max_up=f(i+1,j);//上面
    if (i-1>=1&&inp[i][j]>inp[i-1][j])
        max_down=f(i-1,j);//下面
    max1=max(max_right,max_left);
    max2=max(max_up,max_down);
    data[i][j]=max(max1,max2)+1;//4个方向上的最大值,记录下来
    return data[i][j];
}

int main()
{
    memset(data,0,sizeof(data));
    int k,max_all;
    while(cin>>m>>n)
    {
        max_all=0;
        for(int i=1; i<=m; i++)
            for(int j=1; j<=n; j++)
                cin>>inp[i][j];
        for(int i=1; i<=m; i++)
            for(int j=1; j<=n; j++)
                k=f(i,j);//可以优化,直接计算最大值
        for(int i=1; i<=m; i++)
            for(int j=1; j<=n; j++)
                if (max_all<data[i][j])
                    max_all=data[i][j];
        cout<<max_all<<endl;
    }
    return 0;
}

命运-dp

#include 
using namespace std;
const int maxn = 25;
const int maxm = 1000;
int dp[maxn][maxm],a[maxn][maxm],u[maxn][maxm];
int n,m;
int dfs(int x,int y)
{
    int ans = (-1)*0xfffffff;
    if(u[x][y])
        return dp[x][y];
    if(x<n)
        ans = max(ans,dfs(x+1,y));
    if(y<m)
        ans = max(ans,dfs(x,y+1));
    for(int k=2; k<=m; k++)
        if(y*k<=m)
            ans = max(ans,dfs(x,y*k));
    dp[x][y] = a[x][y] + ans;
    u[x][y] = 1;
    return dp[x][y];
}
int main()
{
    int C;
    cin>>C;
    for(int i=0; i<C; i++)
    {
        cin >>n>>m;
        memset(u,0,sizeof(u));
        for(int j=1; j<=n; j++)
            for(int k=1; k<=m; k++)
                cin>>a[j][k];
        dp[n][m] = a[n][m];
        u[n][m] = 1;
        cout<<dfs(1,1)<<endl;
    }
    return 0;
}

max sum

#include 
using namespace std;
int main()
{
    long long int n,t,i,q,k,st,en,summax,sum,a[100002];
    scanf("%lld",&t);
    for(q=1; q<=t; q++)
    {
        k=1,st=0,en=0,summax=-1000,sum=0;
        scanf("%lld",&n);
        for(i=1; i<=n; i++)
            scanf("%lld",&a[i]);
        for(i=1; i<=n; i++)
        {
            sum+=a[i];
            if(sum>summax)
            {
                summax=sum;
                st=k;
                en=i;
            }
            if(sum<0)
            {
                sum=0;
                k=i+1;
            }
        }
        printf("Case %lld:\n%lld %lld %lld\n",q,summax,st,en);
        if(q!=t)
            printf("\n");
    }
    return 0;
}

最长上升子序列

#include 
using namespace std;
int data[1000];
int dp[1000];
int main()
{
    int n,max;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
            cin>>data[i];
        dp[0]=1;
        for(int i=1; i<n; i++)
        {
            max=0;
            for(int j=0; j<i; j++)
            {
                if (data[j]<data[i])
                {
                    if (max<dp[j])
                        max=dp[j];
                }
            }
            dp[i]=max+1;
        }
        max=0;
        for(int i=0; i<n; i++)
            if (max<dp[i])
                max=dp[i];
        cout<<max<<endl;
    }
    return 0;
}

来来的正方形

#include 
using namespace std;
#define MAX 500
int matrix[MAX][MAX];
int min(int a, int b)
{
    return a < b ? a : b;
}
int main()
{

    int m, n;
    while(~scanf("%d%d", &m,&n))
    {int max = 1;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                scanf("%d", &matrix[i][j]);
        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++)
                if(matrix[i][j] == 1)
                {
                    int mmin = min(matrix[i - 1][j], matrix[i][j - 1]);
                    mmin = min(matrix[i - 1][j - 1], mmin);
                    matrix[i][j] = mmin + 1;
                    if(max < matrix[i][j])
                        max = matrix[i][j];
                }
        printf("%d\n", max);
    }
    return 0;
}

中国象棋

#include 
#include     //用scanf(),printf()输入输出加快速度
#include     //cstring内有memset()函数
using namespace std;
int a[9]= {0,-1,-1,-2,-2,1,1,2,2};   //数组a[]存储马控制的横坐标范围
int b[9]= {0,2,-2,1,-1,2,-2,1,-1};   //数组b[]存储马控制的纵坐标范围,注意相同下标的a,b之间有一定的对应关系,即除了(0,0)外|a|与|b|一个为1,另一个为2
int n,m,x,y,i,j;
int map[21][21];    //map[i][j]表示地图上(i,j)这个点是否是马的控制点
long long tripnum[21][21];    //tripnum[i][j]表示从(0,0)到(i,j)卒合法的行走路线总数
int main()
{
    while(~scanf("%d%d%d%d",&n,&m,&x,&y))
    {
        memset(tripnum,0,sizeof(tripnum));

        for(i=0; i<=20; i++)
            for(j=0; j<=20; j++)
                map[i][j]=1;    //map[i][j]为1时表示(i,j)非控制点
        for(i=0; i<=8; i++)
            if(x+a[i]<=20 && x+a[i]>=0 && y+b[i]<=20 && y+b[i]>=0)    //如果控制点在地图范围内,即这个点存在
                map[x+a[i]][y+b[i]]=0;    //将其设为0,表示这里被马控制
        for(j=0; j<=20; j++)  //从左到右遍历最上面一行的所有点
            if(map[0][j]==1)    //如果这个点不是控制点
                tripnum[0][j]=1;    //那么从(0,0)到达这个点的路线自然只有一条
            else    //否则这点不可到达,路线数为初值0条
                break;    //并且其右的点也不可达,不必继续遍历
        for(i=0; i<=20; i++)  //从上到下遍历最左边一列的所有点
            if(map[i][0]==1)    //如果这个点不是控制点
                tripnum[i][0]=1;    //那么从(0,0)到达这个点的路线自然只有一条
            else    //否则这点不可到达,路线数为初值0条
                break;    //并且其下的点也不可达,不必继续遍历
        for(i=1; i<=n; i++)  //从(1,1)这个点开始,逐行的去看(也可以逐列)
            for(j=1; j<=m; j++)
                if(map[i][j]==1)    //如果这个点不是控制点
                    tripnum[i][j]=tripnum[i-1][j]*map[i-1][j]+tripnum[i][j-1]*map[i][j-1];    //那么到达它的路线数等于其左的点路线与其上的点之和,由于其左与其上的点有可能是控制点,所以要乘以控制系数map[i][j](这也是为什么用0代表控制,而用1代表非控制,而不是调换过来的原因)
        printf("%lld\n",tripnum[n][m]);
    }
    return 0;
}

你可能感兴趣的:(NEFU)