1. 程式人生 > >POJ 2923 【01背包+狀態壓縮/狀壓DP】

POJ 2923 【01背包+狀態壓縮/狀壓DP】

space cert stream The ID ever finally freopen cat

題目鏈接

Emma and Eric are moving to their new house they bought after returning from their honeymoon. Fortunately, they have a few friends helping them relocate. To move the furniture, they only have two compact cars, which complicates everything a bit. Since the furniture does not fit into the cars, Eric wants to put them on top of the cars. However, both cars only support a certain weight on their roof, so they will have to do several trips to transport everything. The schedule for the move is planed like this:

At their old place, they will put furniture on both cars.
Then, they will drive to their new place with the two cars and carry the furniture upstairs.
Finally, everybody will return to their old place and the process continues until everything is moved to the new place.
Note, that the group is always staying together so that they can have more fun and nobody feels lonely. Since the distance between the houses is quite large, Eric wants to make as few trips as possible.

Given the weights wi of each individual piece of furniture and the capacities C1 and C2 of the two cars, how many trips to the new house does the party have to make to move all the furniture? If a car has capacity C, the sum of the weights of all the furniture it loads for one trip can be at most C.

Input
The first line contains the number of scenarios. Each scenario consists of one line containing three numbers n, C1 and C2. C1 and C2 are the capacities of the cars (1 ≤ Ci ≤ 100) and n is the number of pieces of furniture (1 ≤ n ≤ 10). The following line will contain n integers w1, …, wn, the weights of the furniture (1 ≤ wi ≤ 100). It is guaranteed that each piece of furniture can be loaded by at least one of the two cars.

Output
The output for every scenario begins with a line containing “Scenario #i:”, where i is the number of the scenario starting at 1. Then print a single line with the number of trips to the new house they have to make to move all the furniture. Terminate each scenario with a blank line.

Sample Input
2
6 12 13
3 9 13 3 10 11
7 1 100
1 2 33 50 50 67 98
Sample Output
Scenario #1:
2

Scenario #2:
3

【題意】:兩輛車n個物品,每個物品有體積,兩輛車也有體積,要求把物品全部運走最少需要多少次,每次每輛車運送的物體總體積不得大於車的體積。
【分析】:
1.掌握位運算的運算法優先級別很重要
2.掌握基本位運算
(1).判斷是否為0 if((S&1<<i)==0)
(2).將第i位置1 S|1<<i
(3).將第i位置0 S&~(1<<i)
3.要有狀態的思想 每次是對狀態進行操作


預處理所有合法組合...
從合法組合中選取最小的次數。


#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define rep(i,n,x) for(int i=(x); i<(n); i++)
#define in freopen("in.in","r",stdin)
#define out freopen("out.out","w",stdout)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const LL LNF = 1e18;
const int maxn =  1e5 + 5;
const int maxm = 1e6 + 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/*
總的復雜度是O(VNK)
3
5 10 2
1 2 3 4 5
5 4 3 2 1
5 10 12
1 2 3 4 5
5 4 3 2 1
5 10 16
1 2 3 4 5
5 4 3 2 1
*/
int n,m,K;
#define S 100000
#define M 200000
int state[1030];
int dp[1030];
bool vis[1005];
int v1,v2,tot;
int c[12];
bool ok(int x)//判斷一種狀態是否可行(可以一次運走)
{
    int sum = 0;
    ms(vis,0);
    vis[0] = 1;
    for (int i = 0;i < n;i++)
    {
        if ((x>>i)&1)
        {
            sum += c[i];
            for (int j = v1;j >= c[i];j--) //這個真的非常巧妙 開始看半天都不懂,自己模擬一遍才懂
            {                              //比如說此狀態有c1、c2、c3,3個體積,第一次操作把體積c1標記為1,
                  if (vis[j-c[i]])         //第二次操作把c2和c1+c2兩種體積標記為1,第三次把c3和前面的組合標記為1,
                    vis[j] = 1;            //最後這些體積能組合成的所有體積就都被標記成了1
            }
        }
    }
    if (sum > v1+v2 )//裝不下
        return 0;
    //總體積小不代表一定裝得下,拆分成2份要2份都裝得下
    for (int i = 0;i <= v1;i++)
    {
        if (vis[i] && sum-i <= v2)//如果存在(i,sun-i)這樣的組合
            return 1;           //滿足i可以被v1裝下(前面for循環是對於v1的,vis[i]表示體積i可以被v1裝下),sun-i可以被v2裝下
    }
    return 0;
}
void init()//初始化找到滿足條件的狀態
{
    tot = 0;
    for (int i = 1; i < (1<<n); i++)
    {
        dp[i] = INF;
        if (ok(i))
            state[tot++] = i;
    }
}
int main()
{
    int T;
    cin>>T;
    int oo = 0;
    while (T--)
    {
        cin>>n>>v1>>v2;
        for (int i = 0;i < n;i++)
            scanf("%d",&c[i]);
        init();
        int V = (1<<n) - 1;//V是n個1的二進制數
        dp[0] = 0;  //沒有物品當然是0次運走
        for (int i = 0;i < tot;i++)
        {
            for (int j = V;j >= 0;j--)
            {
                if (dp[j] == INF)
                    continue;    //原版的背包是dp[j] = min(dp[j],dp[j-c[i]]+w[i])
                                  //但是顯然二進制不好表示減,但是可以用|抽象加
                                  //這就相當於背包改版成dp[j+c[i]] = min(dp[j+c[i]],dp[j] + w[i])
                if ((j&state[i])==0) //當然2種狀態不能有交集
                {
                    dp[j|state[i]] = min(dp[j|state[i]] ,dp[j] + 1);
                }
            }
        }
        printf("Scenario #%d:\n%d\n",++oo,dp[V]);
        if (T) puts("");
    }
    return 0;
}
// dp[j|s[i]] = min(dp[j|s[i]], dp[j]+1);//在運輸了狀態j上,在運輸s[i], 變成狀態j|s[i]
//如果dp[i-1][j-w[i]]已經出現了(就是說如果存在了重量j-w[i],我只需要填上重量w[i]就可以形成j),那麽dp[i-1][j]就可以組成。
//如果滿足q[i]這種組合的搬運,就需要增加一次搬運dp[j]+1
//然後兩個用或運算合起來,表示一次兩車運的物體。(註意:要把載最重的車能載的情況也看作是一次合)
////vis為第一個背包能裝的情況  //x為狀壓後的一個集合  //在運輸了狀態j上,在運輸s[i], 變成狀態j|s[i]
////01背包因為減法不好用2進制表示,因此選擇加法的DP

POJ 2923 【01背包+狀態壓縮/狀壓DP】