POJ_2923 Relocation(DP)

A - Relocation
Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
Submit

Status

Practice

POJ 2923
Description
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

题解:这道题最开始的时候没有一点思路,虽然知道是状态压缩DP,但是还是觉得无从下手。最后看了题解才把这题A掉,状态压缩是自己之前没有接触过的一类题型,利用这道题再好好的理解一下状态压缩的意义。
状态压缩DP特点
题目本身具有DP的特点,但是一个状态包含的信息太多,无法很简洁的表示,所以用状态压缩来表示状态。状态中的某一维会比较小,一般不会超过15,多了的话状态数会急剧上升而无法压缩,一般来说需要状态压缩的也就是这一维。
状态压缩中的状态转移
状态的设计决定了程序的设计以及代码的长短 。状态方程的转移也需要仔细推敲,不能一带而过,掌握一定的套路,遇到该类型问题能快速建模,专注问题本质。状态压缩DP的关键在于利用好位运算,把握好状态与状态间的关系,按位与(&)按位或(|)按位取反(~)按位异或(^)移位(<<,>>)等需要灵活运用。
状态压缩DP的优化
预处理是最常见的优化,在状态转换前对所有状态进行一个预处理。比如这道题,我们在更新状态转换前先挑选出所有能一次运走的状态,再利用这些状态不断更新他们的和状态,最终得到末状态1……1的最小解。

对于这道题,我们用N位01的二进制数表示家具的搬运状态。首先应该明白无论如何分配每次搬运的状态都应该属于“一次能搬运成功”的状态,而且两个状态间不能有共同搬运家具(按位与!=1)。一个状态并定是由一个或多个可一次搬运成功的子状态按位或构成的。利用这个性质不断更新。
附代码

//定义:dp[i]表示状态i所需要搬运的最少次数,写出状态方程
//dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])。
//O((2^N)*N)
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#define MAX_N 1<<10
#define INF 0xfffffff
using namespace std;

int w[12];
int dp[MAX_N];
int test[MAX_N];
int state[MAX_N];
int c1,c2,N,T;
//检测能否一次运到
bool check(int x);
int main()
{
    cin>>T;
    for( int t = 1; t <= T; t++ )
    {
        scanf("%d%d%d",&N,&c1,&c2);
        for( int i = 0; i < N; i++ )
            scanf("%d",&w[i]);
        int num=0;
        //所有的状态初始值都为无限大
        fill(dp,dp+(1<<N),INF);
        memset(state,0,sizeof(state));
        //先预处理检测出所有能一次运到的。
        for( int i = 0; i < (1<<N); i++ )
        {
            if(check(i))
            {
                state[num]=i;
                dp[i]=1;
                num++;
            }
        }
        for( int i = 0; i < num; i++ )
        {
            //对于每个可一次完成状态也是01背包的关系,倒序遍历
            //1……1=1^N-1
            for( int j = (1<<N)-1; j >= 0; j-- )
            {
                //两个状态间不重叠,因为每一次运输都只可能属于“一次可以运走”状态
                //所以,并定能不断遍历更新到最终状态
                if( !(state[i]&j) )
                    //按位或更新和状态
                    dp[j|state[i]] = min(dp[j|state[i]],dp[j]+1);
            }
        }
        printf("Scenario #%d:\n%d\n\n",t,dp[(1<<N)-1]);
    }
    return 0;
}
bool check(int x)
{
    memset(test,0,sizeof(test));
    int sum=0;
    for( int i = 0; i < N; i++ )
    {
        //检测x各位是否都能取到
        //‘&’表示按位与,x转化为二进制。
        //因为x属于0~1^N-1,所以能将0……0~1……1所有状态都遍历到
        if( x & (1<<i) )
        {
            sum += w[i];//求得所有能取到的和
            //01背包,计算出test[c1]能取到的最大值
            for( int j = c1; j >= w[i]; j-- )
                test[j] = max( test[j],test[j-w[i]]+w[i]);
        }
    }
    //能一次运走
    if( sum - test[c1] <= c2 )
        return true;
    return false;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值