7.8.实验 解题参考

3 篇文章 0 订阅

ProblemA(HDU2084)
这题题目都说了经典DP
由上往下推,由于结果状态多,不好处理。无疑由底往上推比较方便,都归于一个起点,只需要算出由底往上得到的最大价值即可。
方程:dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+a[i][j];

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int a[105][105],dp[105][105];

int main()
{
    int t,n,i,j;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        for(i = 1;i<=n;i++)
        {
            for(j = 1;j<=i;j++)
            scanf("%d",&a[i][j]);
        }
        for(i = n;i>=1;i--)
        {
            for(j = 1;j<=i;j++)
            {
                dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
            }
        }
        printf("%d\n",dp[1][1]);
    }
    return 0;
}

ProblemB(HDU2602)
这是非常典型的01背包问题。
dp[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
状态转移方程:dp[i][v]=max{dp[i-1][v],dp[i-1][v-cost[i]]+value[i]}
这里提醒注意体积为零的情况,如:
1
5 0
2 4 1 5 1
0 0 1 0 0
结果为12

#include<iostream>
using namespace std;
int dp[1000][1000];

int max(int x,int y)
{
    return x>y?x:y;
}

int main()
{
    int t,n,v,i,j;
    int va[1000],vo[1000];
    cin>>t;
    while(t--)
    {
        cin>>n>>v;
        for(i=1;i<=n;i++)
            cin>>va[i];
        for(i=1;i<=n;i++)
            cin>>vo[i];
        memset(dp,0,sizeof(dp));//初始化操作
         for(i=1;i<=n;i++)
        {
            for(j=0;j<=v;j++)
            {
                if(vo[i]<=j)//表示第i个物品将放入大小为j的背包中
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-vo[i]]+va[i]);//第i个物品放入后,那么前i-1个物品可能会放入也可能因为剩余空间不够无法放入
                else //第i个物品无法放入
                    dp[i][j]=dp[i-1][j];
            }
        }
        cout<<dp[n][v]<<endl;
    }
    return 0;
}

当然01背包是可以优化成一维的。
dp[i][v]是由dp[i-1][v]和dp[i-1][v-c[i]]两个子问题递推而来,完全可以对i降维。但一定要注意,必须在每次主循环中以v=V..0的顺序推dp[v],这样才能保证推dp[v]时dp[v-c[i]]保存的是状态dp[i-1][v-c[i]]的值,而没有被覆盖。了解过背包系列算法的应该知道,如果倒过来(0..V)会对每个物品产生叠加,造成重复放置,这正好是解决完全背包(物品数量无限)的算法。

#include<iostream>
using namespace std;
#define Size 1111
int va[Size],vo[Size];
int dp[Size];
int Max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    int t,n,v;
    int i,j;
    cin>>t;
    while(t--)
    {
        cin>>n>>v;
        for(i=1;i<=n;i++)
            cin>>va[i];
        for(i=1;i<=n;i++)
            cin>>vo[i];
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            for(j=v;j>=vo[i];j--)
            {
                dp[j]=Max(dp[j],dp[j-vo[i]]+va[i]); 
            }
        }
        cout<<dp[v]<<endl;
    }
    return 0;
}

ProblemC(HDU1003)
很简单的一维DP,状态转移方程为f[i]=max(f[i-1]+a[i],a[i])
最后扫一遍找最优解。
由于需要位置,很明显最后扫一遍找最优解的时候,找到的那位即为末位置,那么只要在递推的时候顺便记录一下起始位置即可:若a[i]>f[i-1]+a[i],说明要更新,pos[i]=i;否则起始位置不变直接向后加,pos[i]=pos[i-1]。

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

int f[100010],a[100010],pos[100010];

int main()
{
    int T,t,n;
    scanf("%d",&T);t=0;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        f[0]=a[0];pos[0]=0;
        for(int i=1;i<n;i++)
            if(f[i-1]+a[i]>=a[i])
            {
                f[i]=f[i-1]+a[i];
                pos[i]=pos[i-1];
            }
            else
            {
                f[i]=a[i];
                pos[i]=i;
            }
        int j=0;
        for(int i=1;i<n;i++)
            if(f[i]>f[j]) j=i;
        t++;printf("Case %d:\n",t);
        printf("%d %d %d\n",f[j],pos[j]+1,j+1);
        if(T>0) printf("\n");
    }
    return 0;
}

当然这个题其实可以一边做一边找最大值,那么再简化一下其实可以不用数组记录,就变成了用几个变量遍历过去保存最优解的方法。这是由DP进一步抽象过来的,但由于看起来已经不像DP的模式了,这里就不写这种方法咯~

ProblemD(POJ2533)
又是一种非常经典的DP类型,很多题目都可以抽象到这个原型——最长上升子序列。当前点上升序列的长度,可由其前面比它值小的点加一得到,那么最大的长度即比较前面所有能与它连起来的情况。
转移方程:f[i]=max{f[j]}+1; ( 0 <= j < i && a[j] < a[i] )
初值f[i]=1。

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

int f[100010],a[100010];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        f[i]=1;
    }
    for(int i=1;i<n;i++)
        for(int j=0;j<i;j++)
        if(a[j]<a[i] && f[j]+1>f[i])
            f[i]=f[j]+1;
    int ans=0;
    for(int i=0;i<n;i++)
        if(f[i]>ans) ans=f[i];
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值