《紫书:动态规划初步》总结二

多阶图的最短路

对于一个数字矩阵,从左往右走,每次可以直右,右上,右下。走到最后的数字和最小为多少。

同时输出字典序最小的走过的行号。

这里有n^2的状态,但可以容易发现,每一列的n个状态可以划分为一个阶段,实质上是阶段之间的转移

枚举每个阶段,对于每个阶段的每个状态分别转移即可。

dp[i]][j]表示从i、j开始走到最后的最小和,这是满足最优子结构的。

维护每个状态下一次最优路径的最小行号。(遍历排序即可)。这种链表式的答案,可以保证局部字典序最小,实现全局最小。

[可能你这边比较小,但是答案前面的更小,我从前面的开始是不会经过后面较小的(如果后面较小的前面较大)]。

核心代码:

        for(int j=c;j>=1;j--)
            for(int i=1;i<=r;i++){
                if(j==c)dp[i][j]=A[i][j];
                else{
                    int rows[3]={i,i-1,i+1};
                    if(i==1)rows[1]=r;
                    if(i==r)rows[2]=1;
                    sort(rows,rows+3);
                    for(int k=0;k<3;k++){
                        int v=dp[rows[k]][j+1]+A[i][j];
                        if(v<dp[i][j]){
                            dp[i][j]=v;ans[i][j]=rows[k];
                        }
                    }
                }
                if(j==1&&dp[i][1]<res){first=i;res=dp[i][1];}
            }
        printf("%d",first);
        for(int i=ans[first][1],j=2;j<=c;i=ans[i][j],j++){
            printf(" %d",i);
        }
        printf("\n%d\n",res);

0-1 背包问题

1、物品无限的背包问题

类似于硬币问题:C->C-V[I]的状态,dp[C]=max(dp[C-V[i]]+W[i]);

2、0-1背包问题

每个物品只能取一次。

所以不能重复遍历了,因为你也不知道哪些已经被选了(状压?数字大的时候没办法状压)

考虑阶段的问题,在把前i个物品放进背包里属于第i个阶段,每个阶段切换到下一个阶段只用判断要不要放新的物品即可。

每个阶段有背包为C以内的状态。dp[i][C]=max(dp[i-1][C-V[i]])。如果我要选择第i个物品,那么对于之前的背包状态必然只有C-V[i].

对内存继续优化,可以考虑滚动数组,因为:状态转移方程算C只会用到C-v[i].那么也就是说,算dp[i][C]的时候,dp[i][C]并没有更新,那么把i去掉,每次计算后面的没更新,还是i-1的阶段

Jin Ge Jin Qu hao:UVA - 12563 

题意:在时间t-1内选择足够多的歌曲,如果有多个选择,选择总歌曲时长最大的。

就是在t-1大的背包里放尽可能多个的歌曲,维护两个背包,一个记录歌曲数量,另一个记录歌曲总时长。

但是背包取优先级是先数量,数量相同的情况再去取总时长更大的。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long
#define M 404
#define inf 0x3f3f3f3f

int T,n,V;
int dp[181*55],pd[181*55];

int main(){
    cin>>T;
    int cnt=0;
    while(T--){
        scanf("%d%d",&n,&V);
        memset(dp,0,sizeof(dp));
        memset(pd,0,sizeof(pd));
        for(int i=1;i<=n;i++){
            int w;scanf("%d",&w);
            for(int j=V-1;j>=0;j--)if(j>=w){
                if((dp[j-w]+1>dp[j])||(dp[j-w]+1==dp[j]&&pd[j-w]+w>pd[j])){
                    dp[j]=dp[j-w]+1;
                    pd[j]=pd[j-w]+w;
                }
            }
        }
        printf("Case %d: %d %d\n",++cnt,dp[V-1]+1,pd[V-1]+678);
    }
}

这里突然想到前天训练赛没做出来的一道01背包题目:

题意:有n块砖,已知长和宽,选择一些把宽朝上作为第一层,在第二次把剩下让长朝上。

保证总长度小于第一层,求解让第一层尽可能的短,最短长度。必须用完所有砖。

假如取了1、2作为第一层,3、4、5作为第二层。

a1+a2>=b3+b4+b5;

∑ai-a3-a4-a5>=b3+b4+b5;

∑ai>=a3+b3 + a4+b4 + a5+b5;

必须满足这样的关系,并且使得3、4、5的a3、a4、a5尽可能大,这样的话∑a-a3-a4-a5会尽可能的小。

相等于有n个物品,体积为ai+bi,价值为ai,背包大小为ai的和,往里面 放尽可能多的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值