动态规划练习第一弹

苦练动态规划中,现在还处于玄学推转移方程的阶段,得多多学习别人的思想。

(1)巴比伦塔——UVa 437

         题目链接:https://vjudge.net/contest/232313#problem/B

         紫书上的dp例题,思路也很清晰易懂:一个立方体最多只有三个不同的朝上的面,所以最多也只有90种面,而只有顶面的尺寸会影响到后续的决策,所以用动态规划处理,转换成DAG上的最长路,dp[i][j]表示第i个立方体到第j个立方体的最大高度,G[i][j]表示DAG的邻接矩阵,即表示可不可以在上面,套用紫书上DAG模型的模板就行。具体细节看代码。

    

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

typedef long long ll;

const int maxn=95;

int n;

ll dp[maxn][maxn];//表示第i个立方体到第j个立方体的最大高度

int G[maxn][maxn];//DAG上的dp

struct Cobe
{
    ll x,y,z;
    Cobe(ll _x=0,ll _y=0,ll _z=0):x(_x),y(_y),z(_z){}
};
Cobe b[maxn];

//是否可以在上面
bool judge(Cobe a,Cobe b)
{
    if(a.x>b.x&&a.y>b.y)
        return true;
    if(a.x>b.y&&a.y>b.x)
        return true;
    return false;
}

void handle()
{
    memset(G,0,sizeof(G));
    for(int i=0;i<n*3;i++)
    {
        for(int j=0;j<n*3;j++)
        {
            if(i!=j&&judge(b[i],b[j]))
                G[i][j]=1;//可选状态
        }
    }
}

ll DP(int x1,int y1)
{
    ll& ans=dp[x1][y1];
    if(ans!=-1)
    {
        return ans;
    }
    for(int i=0;i<n*3;i++)
    {
        if(y1==i)continue;
        if(G[y1][i])ans=max(ans,DP(y1,i)+b[x1].z);
    }
    if(ans==-1)
        ans=b[x1].z+b[y1].z;//最起码能到达
    return ans;
}

int main()
{
    //freopen("E:\\test_data\\in.txt","r",stdin);
    //freopen("E:\\test_data\\out.txt","w",stdout);
    int index=1;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        int tot=0;
        ll x,y,z;
        for(int i=0;i<3*n;i+=3)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            b[i].z=z;//高
            b[i].x=x;
            b[i].y=y;
            b[i+1].z=x;//高
            b[i+1].x=y;
            b[i+1].y=z;
            b[i+2].z=y;//高
            b[i+2].x=x;
            b[i+2].y=z;
        }
        handle();
        ll ans=0;
        memset(dp,-1,sizeof(dp));
        for(int i=0;i<n*3;i++)
        {
            for(int j=0;j<n*3;j++)
            {
                if(G[i][j])
                    ans=max(ans,DP(i,j));
            }
        }
        printf("Case %d: maximum height = %lld\n",index++,ans);
    }
    return 0;
}

(2)旅行——UVa1347

         题目链接:https://vjudge.net/contest/232313#problem/C

         也是刘汝佳紫书上的题,将问题改成两个人同时从最左边点出发,每个人通过不同的点到达最右点的方案。dp[i][j]表示1~max(i,j)都被访问过,且两人当前的位置分别是i和j,还要走多长的距离。这样,我们再加上一个移动的条件:只允许其中一个人走到i+1,而不是i+2,i+3......这样我们就能确保每个点都访问过且不丢失最优解。状态dp[i][j]也就只能移动到dp[i+1][j]和dp[i+1,i](一个表示第一个人访问i+1,一个表示第二个人访问i+1)。具体细节看代码。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
#include <iomanip>
#include <cmath>
using namespace std;
/*
    模型转化为起始点有两个人A,B,分别走不同的点后到达最右边的点,
    dp[i][j]表示1~max(i,j)都被走过之后,还需要走的最小距离,
    每个点必须由A,B两个中的一个路过,这样dp公式就出来了
    dp[i][j]=min(DP(i+1,j)+distance(p[i],p[i+1],DP(i+1,i)+distance(p[j],p[i+1])))
    前一个表示A走过i+1这个点,后一个表示B走过i+1这个点
*/

typedef long long ll;

const double pi = acos(-1.0);

int N;

struct Point
{
    int x,y;
    Point(int _x=0,int _y=0):x(_x),y(_y){}
};
Point p[1005];
double dp[1005][1005];//A在i,B在j(i>=j)且i之前的所有点都走过了的情况下,还需走的最小值

double dist(Point &a,Point &b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double DP(int i,int j)
{
    if(dp[i][j]>0)
        return dp[i][j];
    return dp[i][j]=min(DP(i+1,j)+dist(p[i],p[i+1]),DP(i+1,i)+dist(p[j],p[i+1]));
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int tot=0;
        double x,y;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&x,&y);
            p[++tot]=Point(x,y);
        }
        memset(dp,0.0,sizeof(dp));
        for(int i=1;i<n-1;i++)
            dp[n-1][i]=dist(p[n-1],p[n])+dist(p[i],p[n]);//起始状态
        double ans=DP(1,1);
        printf("%.2lf\n",ans);
    }
    return 0;
}

   (3)劲歌金曲——UVa12563

            题目链接:https://vjudge.net/contest/232313#problem/E

            典型的01背包问题,不过注意一点:首先考虑歌曲数目最多,其次才是在这个基础上时间尽可能的长。因此,dp[i][j]表示前i首歌在j秒能唱的歌的最大数目,tim[i][j]表示前i首歌在剩余j时间内能唱的最长时间,这两个都是典型的01背包问题,直接调用模板转移就行,不过要记住之前说的先保证数量,再保证时间。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

typedef long long ll;

const int maxn=50+5;
int A[maxn];//歌曲时间
int dp[maxn][10005];//前i个在j秒能唱的歌的最大数目
int tim[maxn][10005];//i首歌后时间为j的最大时长
//bool vis[maxn];

int main()
{
    int T;
    scanf("%d",&T);
    int index=1;
    while(T--)
    {
        int n,t;
        scanf("%d%d",&n,&t);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
        }
        for(int i=0;i<=t;i++)
        {
            dp[0][i]=0;//初始条件
            tim[0][i]=0;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=t;j++)
            {
                dp[i][j]=(i==1?0:dp[i-1][j]);
                tim[i][j]=(i==1?0:tim[i-1][j]);
                if(j>A[i])
                {
                    //先保证数量,再保证时间,所以必须先判断是否可以多一首
                    //再判断不能再多一首的情况下的最大时间
                    if(dp[i][j]<(dp[i-1][j-A[i]]+1))
                    {
                        dp[i][j]=max(dp[i][j],dp[i-1][j-A[i]]+1);
                        tim[i][j]=tim[i-1][j-A[i]]+A[i];
                    }
                    else if(dp[i][j]==(dp[i-1][j-A[i]]+1))
                    {
                        tim[i][j]=max(tim[i][j],tim[i-1][j-A[i]]+A[i]);
                    }
                }
            }
        }
        printf("Case %d: %d %d\n",index++,dp[n][t]+1,tim[n][t]+678);
    }
    return 0;
}

 (4)牛客小白月赛7——G

          题目链接:https://www.nowcoder.com/acm/contest/190/G

          一个二分背包的问题,因为尽量要公平,则每个人的质量要尽可能靠近总质量/2,所以将总质量的一半作为背包容量,这就变成01背包问题了,最后少的就是wavator,多的就是小姐姐的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

/*
     将总质量的一半作为背包,就变成01背包题
*/
const int maxn=105;
int A[maxn];
int dp[maxn][10005];//表示前i个苹果装入容量为j的背包的最大质量

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int totw=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
            totw+=A[i];
        }
        int t=totw/2;
        for(int i=0;i<=t;i++)
        {
            dp[0][i]=0;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=t;j++)
            {
                dp[i][j]=dp[i-1][j];
                if(j>=A[i])
                {
                    dp[i][j]=max(dp[i][j],dp[i-1][j-A[i]]+A[i]);
                }
            }
        }
        printf("%d %d\n",dp[n][t],totw-dp[n][t]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Q755100802/article/details/82843981