背包九讲

2018-05-06 13:35:32

感谢: https://blog.csdn.net/ling_du/article/details/41594767

一讲:01背包问题

问题描述:给定一个袋子的容积是v,给定n种物品,每种物品有两种属性,价值val和体积vol,每种物品只有一个,问如何选择可以使得得到的价值最大

                 我们知道每个物品只有选或不选两种状态,那么为什么不可以遍历每种状态呢,此时,如果有n种物品,那么就需要求2^n种情况的值

                 那么01背包的时间复杂度是多少呢?其实01背包是一种DP  时间复杂度分析完再说

思路:对于每一个物品 都有选或不选两种状态  设置数组dp[i][v]表示前i件物品恰放进一个体积为v的背包得到得最大价值  第i件物品放或不放得到的价值为什么不能直接由前i-1件物品放或不放得到呢  为什么还与体积有关  因为可能我们现在虽然价值得到了最大 但是没有节约体积 以至于后面的价值大的物品  不能放进去 据失去了最优解 于是记录一下体积 就会防止发生这种情况

转移方程如下: 当第i件物品放入体积为v的背包中是  证明前i-1件物品放在了体积为v-vol[i]中  于是dp[i][v]=dp[i-1][v-vol[i]]  当第i件物品不放进体积为v的背包中时 dp[i][v] = dp[i-1][v]  求最大值

伪代码:

for(int i=1; i<=n; i++)
{
    for(int v=1; v<V; v++)
    {
        dp[i][v] = max(dp[i-1][v-vol[i]]+val[i] , dp[i-1][v]);放或不放
    }
}
01背包伪代码

  此时的时间复杂度为O(n*v)  暴力的时间是指数级的  这个时间是正比级的  在n大的情况下省很多时间

优化:时间上做不到优化  空间上可以有优化  此时的空间复杂的也是O(n*v),但是可以优化到O(v) 我们看上面的转移方程

          dp[i]求的时候  在二维数组的第一维上只与i-1有关 于是我们可以省掉第一维 只开dp[v] 但是由于与i-1有关 于是我们采取反向遍历就好

例题:以 Just another Robbery  LightOJ - 1079为例  多了一个难点就是这个是概率的01背包  其实也是写到这个题才想要看一下背包九讲的  兴致勃勃告诉队友我要去学习概率DP了  期待晚上告诉他们我看完了背包九讲时他们的态度  嘻嘻

题意:小花要去强银行  自己危险值的上限是p 有n家银行 每家银行有对应的价值和危险值 问最多可以抢多少钱 

代码如下:

#include<stdio.h>
#include<iostream>

using namespace std;

int t;
int n;
double P;
int v[110];
double p[110];
double dp[105][105*105];
int sum;

void init()
{
    sum = 0;
}

void input()
{
    for(int i=1; i<=n; i++)
    {
        scanf("%d%lf" , &v[i] , &p[i]);
        sum += v[i];
    }
}

void solve()
{
    /*
    dp[i][j] 记录的是抢了i个银行 得到了j元钱 被抓的概率
    */
    for(int i=1; i<=sum; i++)
        dp[0][i] = -1;  //抢了0个银行  得到j元钱是不可能的 j>0
    dp[0][0] = 0;       //抢了0个银行  得到0元钱 被抓的概率是0
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=sum; j++)
        {
            /*
            因为dp初始值都为0  所以只有在i-1=0的时候才会出现小于-0.5 / <0的情况
            */
            if(j<v[i] || dp[i-1][j-v[i]]<=-1)  //第i个银行强不了(j<v[i])  或者虽然第i个银行可以抢 但是如果抢了他  他前面的九不成立了  也就是虽然可以建二楼 但是你一楼就垮了 所以二楼还是一楼
                dp[i][j] = dp[i-1][j];
            else if(dp[i-1][j]<0)
                dp[i][j] = dp[i-1][j-v[i]] + (1-dp[i-1][j-v[i]])*p[i]; //第i个银行可以抢 因为j>v[i] if里对应抢i-1个银行得不到j元钱的情况 此时第i个银行一定得抢
            else
                dp[i][j] = min(dp[i-1][j], dp[i-1][j-v[i]]+(1-dp[i-1][j-v[i]])*p[i]);   //前i-1次被抓的概率是dp[i-1][j-v[i]] 于是不被抓的概率就是1-dp[i-1][j-v[i]] 再乘上此时被抓的概率
        }
    }
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%lf%d" , &P , &n);
        init();
        input();
        solve();
       int ans = 0;
//       for(int j=0; j<=n; j++)
//       {
       for(int i=0; i<=sum; i++)
       {
           if(dp[n][i]>-1 && dp[n][i]<P)
            ans = i;
//                printf("%lf..." , dp[j][i]);
       }
//        printf("\n");
//       }
       printf("Case %d: %d\n" , cas , ans);
    }


    return 0;
}
View Code

 

posted @ 2018-05-06 15:20  Flower_Z  阅读(156)  评论(0编辑  收藏  举报