zoukankan      html  css  js  c++  java
  • POJ 2923 Relocation(状压DP+01背包)题解

    题意:给你汽车容积c1,c2,再给你n个包裹的体积,问你最少运几次能全运走

    思路:用2进制表示每次运送时某物在不在此次运送之中,1在0不在。我们把运送次数抽象成物品价值,把状态抽象成体积,用一个dp[ i ] 记录完成状态i的最少步数那么就转化为了01背包问题,得到状态转移方程dp[ j|state ] = min( dp[ j|state ],dp[j] + 1 ),state为运送时物品的状态。

    然后讲一下可能会有点看不懂的judge()的一段代码

    for(int j = c1;j >= val[i];j--){    //将所有可能放进c1的组合标记为1
        if(vis[j - val[i]])
            vis[j] = 1;
    }

    这里的意思是将所有能放进c1的组合标记为1,他是这样运作的:先将vis[0] = 1,这样每次j到val[i]时,val[i]肯定会被置为1,因为是从c1开始往下搜,如果搜到一个vis[j - val[i]] = 1,这说明 j-val[i] 和 val[i] 能做成一个体积为j(j<=c1)的组合,可见j能塞进c1,所以置为1,这样一直搜索就能搜到所有的组合

    参考题解

    代码:

    #include<cstdio>
    #include<map>
    #include<set>
    #include<queue>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    const int maxn = 1 << 10;
    const int MOD = 100000000;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    int val[12],state[maxn],vis[maxn];  //vis[i] = 1代表重量为i的组合能塞进车c1
    int dp[maxn];   //达成i状态最小步骤
    int tot,n,c1,c2;
    int judge(int x){   //判断能否一次运走
        int sum = 0;
        memset(vis,0,sizeof(vis));
        vis[0] = 1;
        for(int i = 0;i < n;i++){
            if(x&1<<i){
                sum += val[i];
                for(int j = c1;j >= val[i];j--){
                //将所有可能放进c1的组合标记为1
                    if(vis[j - val[i]])
                        vis[j] = 1;
                }
            }
        }
        if(sum > c1+c2) return 0;   //总体积大于两车总容积
        for(int i = 0;i <= c1;i++){
            if(vis[i] && sum - i <= c2){
            //只要有一种分组能让两辆车都能塞进两种组合
                return 1;
            }
        }
        return 0;
    }
    void init(){    //初始化,找到所有能运送的状态
        tot = 0;
        for(int i = 1;i < (1<<n);i++){
            dp[i] = INF;
            if(judge(i)){
                state[tot++] = i;
            }
        }
    }
    int main(){
        int T,Case = 1;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&n,&c1,&c2);
            for(int i = 0;i < n;i++)
                scanf("%d",&val[i]);
            init();
            int V = (1<<n) - 1;
            dp[0] = 0;
            for(int i = 0;i < tot;i++){
                for(int j = V;j >= 0;j--){
                    if(dp[j] == INF) continue;
                    if(j&state[i]) continue;
                    //有交集,不能送第二次
                    dp[j|state[i]] = min(dp[j|state[i]],dp[j] + 1);
                }
            }
            printf("Scenario #%d:
    %d
    
    ",Case++,dp[(1<<n) - 1]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    vue-music 关于搜索历史本地存储
    Node.js中package.json中^和~的区别
    主流浏览器内核
    pm2 日常使用
    前端验证用户登陆状态(vue.js)
    ng-repeat循环出来的部分调用同一个函数并且实现每个模块之间不能相互干扰
    行内元素垂直方向位置调整的一些感悟和困惑
    由overflow-x:scroll产生的收获
    子代选择器(>)后代选择器(' ')的区别
    content相关属性
  • 原文地址:https://www.cnblogs.com/KirinSB/p/9408765.html
Copyright © 2011-2022 走看看