7.8.实验 解题参考

来源:互联网 发布:usb虚拟打印机端口 编辑:程序博客网 时间:2024/06/10 01:37

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 1111int 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
原创粉丝点击