• 背包九讲


    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("
    ");
    //       }
           printf("Case %d: %d
    " , cas , ans);
        }
    
    
        return 0;
    }
    View Code
  • 相关阅读:
    android binder机制详解
    java 类继承,父类子类方法调用的过程d
    java 类继承,父类子类方法调用的过程
    android 多点触控
    SurfaceView 游戏开发的一些基础知识和注意事项
    android handler异步处理机制详解(线程局部存储(TLS)知识点)
    android 类加载器 DexClassLoader的用法,以及引出的插件架构
    java 基础知识点
    jdk8 lambda表达式list操作分组、过滤、求和、最值、排序、去重
    Spring MVC自定义消息转换器(可解决Long类型数据传入前端精度丢失的问题)
  • 原文地址:https://www.cnblogs.com/Flower-Z/p/8998356.html
Copyright © 2020-2023  润新知