ACM新手DAY 14 简单动态规划(背包、LIS等)

题解

A - Common Subsequence

题目: 求最长公共子序列

  • LCS
//LCS
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int INF = 1<<30;
const int maxn = 1010;
int dp[maxn][maxn];
int main()
{
    string s1, s2;
    while(cin >>s1 >>s2)
    {
        memset(dp, 0, sizeof dp);//dp初始化

        for(int i=0; i<s1.size(); i++)//记录每个状态的最长
            for(int j=0; j<s2.size(); j++)
            {
                if(s1[i]==s2[j])
                    if(i>0 && j>0)
                        dp[i][j] = dp[i-1][j-1]+1;
                    else
                        dp[i][j] = 1;
                if(i > 0)//甄选长度部分
                    dp[i][j] = max(dp[i][j], dp[i-1][j]);
                if(j > 0)
                    dp[i][j] = max(dp[i][j], dp[i][j-1]);
            }

        int ans=0;
        for(int i=0; i<s1.size(); i++)//找到最长
            for(int j=0; j<s2.size(); j++)
                ans = max(ans, dp[i][j]);
        cout <<ans <<endl;
    }

    return 0;
}

B - Super Jumping! Jumping! Jumping!

题意:最长上升子序列

  • LIS
  • 逻辑部分的变化
//LIS
        memset(dp, 0, sizeof dp);//dp初始化
        for(int i=0; i<n; i++) cin >> num[i]; //读入数组
        dp[0] = num[0];
        for(int i=0; i<n; i++)//dp状态记录
        {
            int flag =0;
            for(int j =0; j<i; j++)
                if(num[i] > num[j])
                {
                    dp[i] = max(dp[i],dp[j] + num[i]);
                    flag =1;
                }
            if(!flag)
                dp[i] = num[i];
        }
        int mx=0;
        for(int i=0; i<n; i++)//遍历找最大
            mx = max(mx, dp[i]);
        cout << mx << endl;

C - Bone Collector

  • 0-1背包,不需要恰好装满,也算PPT里面体积为1那种情况吧
//0-1背包,一维数组,不需要恰好装满
#include<bits/stdc++.h>
using namespace std;
const int INF = 1<<30;
const int maxn = 1010;
int dp[maxn], v[maxn], w[maxn];
int main()
{
    int T;
    cin >>T;
    while(T--)
    {
        int N, V;
        cin >>N >>V;
        for(int i=1; i<=N; i++) cin >>w[i];//读价值
        for(int i=1; i<=N; i++) cin >>v[i];//读体积
        memset(dp, INF, sizeof dp);//dp初始化,不用恰好装满
        for(int i=v[1]; i<=V; i++)
            dp[i] = w[1];

        for(int i=2; i<=N; i++)
            for(int j=V; j>=v[i]; j--)
                dp[j] = max(dp[j], dp[j-v[i]]+w[i]);

        cout <<dp[V] <<endl;
    }
    return 0;
}

D - 饭卡

题目:电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。 某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。

  • 找到最贵的菜,最后买,然后类似0-1背包,逼近5元余额
//先找到最贵的菜,放到最后买,像0-1背包的一维数组
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int dp[maxn], w[maxn];
int main()
{
    int n, m;
    while(cin >> n && n)
    {
        memset(dp, 0, sizeof dp);
        for(int i=0; i<n; i++) cin >> w[i];//读入价值
        sort(w,w+n);//升序排序
        cin >> m;
        for(int i=0; i<n-1; i++)//对花费的无限逼近
        {
            for(int j=m-5; j>=w[i]; j--)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
            }
        }
        int ans =  dp[m-5];
        if(m<5) cout << m << endl;
        else cout << m-ans-w[n-1] << endl;//w[n-1]放在最后买
    }
    return 0;
}

F - 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

  • 多重背包
//多重背包
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int dp[maxn], num[maxn], weight[maxn], val[maxn];
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n, m;
        cin >> n >> m;
        memset(dp, 0, sizeof dp);
        for(int i=1; i<=m; i++)
        {
            cin >> num[i] >> weight[i] >> val[i];
            for(int j=0; j<val[i]; j++)
                for(int k=n; k>=num[i]; k--)
                    dp[k]=max(dp[k],dp[k-num[i]]+weight[i]);
        }
        cout<<dp[n]<<endl;
    }
    return 0;
}

E - Piggy-Bank(补题)

  • 完全背包
  • 不过价值最大改成了求价值最小
  • 自己的超时的代码
//完全背包,且求的是价值最小
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;//反转的时候这里也变了,变成最大了!!
const int maxn = 510;
int w[maxn], v[maxn], dp[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,inf,sizeof(dp));
        dp[0]=0;
        int a, b;
        scanf("%d%d",&a,&b);
        int m=b-a;
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            cin>>v[i]>>w[i];
        for(int i=1; i<=n; i++)
        {
            for(int j=w[i]; j<=m; j++)
                dp[j]=min(dp[j],dp[j-w[i]]+v[i]);//用dp存的是钱的数目
        }
        if(dp[m]==inf)printf("This is impossible.\n");
        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[m]);
    }
    return 0;
}
  • 别人的AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 0x3f3f3f3f
using namespace std;
//#define maxn 0x3f3f3f3f
int t,e,f,n;
int p[505], w[505], dp[10005];
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&e,&f);
        scanf("%d",&n);
        for (int i = 0; i < n; i += 1)  scanf("%d%d",&p[i],&w[i]);
        for (int i = 0; i <= f-e; i += 1)   dp[i] = maxn;
        dp[0] = 0;
        for (int i = 0; i < n; i += 1)
            for (int j = w[i]; j <= f-e; j += 1)
                dp[j] = min(dp[j],dp[j-w[i]]+p[i]);
        if (dp[f-e] != maxn) printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f-e]);
        else printf("This is impossible.\n");
    }
    return 0;
}

I - 数数塔

题目:有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?并输出最大和的路径。在这里插入图片描述

  • 动态规划
  • 从倒数第二行起往上,找每一个点的下面对应的两个里面最大的,这样往上找去,直到最后只剩一个点。对于输出路线和判断路线数目的要求,可在代码里体现。
#include <iostream>
using namespace std;
const int mx = 100;
struct node
{
    int num, dp, dir;
    int bl;
} a[mx][mx];//num用来记录每个点的数,dp是动态规划用的,dir是记录方向,用来输出坐标的,bl是判断路径是否有多条的
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        for(int i = 0; i < n; i++)//输入数塔
        {
            for(int j = 0; j <= i; j++)
            {
                cin >> a[i][j].num;
                a[i][j].dp = a[i][j].num;
                a[i][j].dir = 0;
                a[i][j].bl = 0;
            }
        }
        int bl = 0, bl2 = 0, cou(0);
        int tmp;
        for(int i = n-2; i >= 0; i--)//从倒数第二行开始遍历啦
        {
            for(int j=0; j <= i; j++)
            {
                if (a[i+1][j].dp > a[i+1][j+1].dp)	//左边大
                {
                    a[i][j].dir = 0;		//选择左边
                    a[i][j].dp += a[i+1][j].dp;//dp计数
                    if(a[i+1][j].bl)
                        a[i][j].bl = 1;
                }
                else
                {
                    if(a[i+1][j].dp == a[i+1][j+1].dp)
                    {
                        a[i][j].bl = 1;
                    }
                    a[i][j].dir = 1;
                    a[i][j].dp += a[i+1][j+1].dp;
                    if(a[i+1][j+1].bl)
                        a[i][j].bl = 1;
                }
            }
        }
        cout << a[0][0].dp << endl;//最大值
        if(a[0][0].bl) cout << "There are several solutions!" <<endl;
        else
            for(int i = 0, j = 0; i< n; i++)
            {
                cout << "(" << i << ", " << j << ")" ;
                if(i+1 < n) cout << " -> ";
                else cout << endl;
                j += a[i][j].dir;
            }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值