zoukankan      html  css  js  c++  java
  • POJ-2923 Relocation---01背包+状态压缩

    题目链接:

    https://vjudge.net/problem/POJ-2923

    题目大意:

    有n个货物,给出每个货物的重量,每次用容量为c1,c2的火车运输,问最少需要运送多少次可以将货物运完

    思路:

    第一次做状态压缩(状态压缩基础知识传送门

    本题的解题思路是先枚举选择若干个时的状态,总状态量为1<<n,判断这些状态集合里的那些物品能否一次就运走,如果能运走,那就把这个状态看成一个物品。预处理完能从枚举中找到tot个物品,再用这tol个物品中没有交集(也就是两个状态不能同时含有一个物品)的物品进行01背包,每个物品的体积是state[i](state[i]表示一次可以运完状态i的物品,i的二进制表示i这个状态的物品),价值是1,求包含n个物品的最少价值也就是dp[(1<<n)-1](dp[i]表示状态i需要运的最少次数)。

    状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i],1<=j<=(1<<n)-1])。

     1 #include<iostream>
     2 #include<vector>
     3 #include<queue>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cstdio>
     7 #include<set>
     8 #include<cmath>
     9 using namespace std;
    10 typedef pair<int, int> Pair;
    11 typedef long long ll;
    12 const int INF = 0x3f3f3f3f;
    13 const int maxn = 2000+10;
    14 int T, n, m1, m2;
    15 int a[20], cnt[maxn], dp[maxn], tot, cases;
    16 bool vis[maxn];
    17 bool judge(int x)
    18 {
    19     int sum = 0;
    20     memset(vis, 0, sizeof(vis));//vis[i]=1表示m1的车子中可以凑出体积为i的物品
    21     vis[0] = 1;
    22     for(int i = 0; i < n; i++)
    23     {
    24         if(x & (1 << i))//第i件物品存在
    25         {
    26             sum += a[i];
    27             for(int j = m1; j >= a[i]; j--)
    28                 if(vis[j - a[i]])vis[j] = 1;//此处必须是逆序,因为更新vis[j]的时候要用到vis[j-a[i]],和01背包是一样的
    29         }
    30     }
    31     for(int i = 0; i <= m1; i++)
    32     {
    33         if(vis[i] && sum - i <= m2)//确保全部物品可以一次性放在两个车子里面
    34             return true;
    35     }
    36     return false;
    37 }
    38 void init()
    39 {
    40     memset(dp, INF, sizeof(dp));
    41     dp[0] = 0;
    42     for(int i = 1; i < (1 << n); i++)
    43     {
    44         if(judge(i))
    45         {
    46             cnt[tot++] = i;
    47         }
    48     }
    49 }
    50 int main()
    51 {
    52     cin >> T;
    53     while(T--)
    54     {
    55         cin >> n >> m1 >> m2;
    56         tot = 0;
    57         for(int i = 0; i < n; i++)cin >> a[i];
    58         init();/*
    59         for(int i = 0; i < tot; i++)
    60             cout<<cnt[i]<<endl;*/
    61         for(int i = 0; i < tot; i++)//枚举物品
    62         {
    63             for(int j = (1 << n) - 1; j >= 0; j--)//逆序枚举状态也是因为dp[j]的更新需要先用到dp[j-***]
    64             {
    65                 if(dp[j] == INF)continue;
    66                 if((j & cnt[i]) == 0)//两者无交集
    67                     dp[j | cnt[i]] = min(dp[j | cnt[i]], dp[j] + 1);
    68                 //dp[j | cnt[i]]表示j这个状态加上第i件物品的值,可以从dp[j]+1推过去
    69             }
    70         }
    71         printf("Scenario #%d:
    ", ++cases);
    72         printf("%d
    
    ", dp[(1<<n) - 1]);
    73 
    74     }
    75 }
  • 相关阅读:
    java操作redis之jedis篇
    实现指定步长循环后移字符串数组算法
    【PAT Advanced Level】1006. Sign In and Sign Out (25)
    银行计算利息
    中国人、美国人、北京人
    网络子系统55_ip协议分片重组_加入ipq
    C#拦截系统消息的方法-Application.AddMessageFilter
    C#实现在Form上截取消息的两种方法
    Geek改变世界
    中国黑客传说:游走在黑暗中的精灵
  • 原文地址:https://www.cnblogs.com/fzl194/p/8810020.html
Copyright © 2011-2022 走看看