[POJ 2923] Relocation (动态规划 状态压缩)

时间:2022-06-02 20:53:06

题目链接:http://poj.org/problem?id=2923

题目的大概意思是,有两辆车a和b,a车的最大承重为A,b车的最大承重为B。有n个家具需要从一个地方搬运到另一个地方,两辆车同时开,问最少需要搬运几次?

我先想的是我由A车开始搬,搬运能装的最大的家具总重,然后状态压缩记录下搬运了哪些,然后再由B车搬运,状态压缩记录搬运了哪些。以此类推,直到状态满了。

以上方法TLE

然后,实在想不出来了,看了题解:http://blog.csdn.net/woshi250hua/article/details/7636061

也就是说先确定哪些状态是两辆车一次可以搬完的。

怎么确定呢?首先我们先确定出给定状态集合中所有可能出现的并且可以被a车运送的重量,然后看是否存在总重量减去可被a车运送的重量所得到的剩余重量是否能够被b车运送。

如果可以,则这个状态是可以被a,b两辆车一次就送完的。

那么怎么确定给定状态集合中所有可能出现的并且可以被a车运送的重量呢?

这又是一个dp。

设计状态dp[i][j]表示状态集合中的前i个物品是否能够组成j这个重量

如果dp[i-1][j-w[i]]已经出现了(就是说如果存在了重量j-w[i],我只需要填上重量w[i]就可以形成j),那么dp[i-1][j]就可以组成。

这个问题结束。

对于总问题:

设计状态dp[i][j]表示从前i种已知可以一次搬完的里面搬状态j需要的最少次数。

状态转移:dp[i][j|mask[i]] = min(dp[i-1][j]+1,dp[i-1][j|mask[i]])

 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <iterator>
#include <vector>
using namespace std;
typedef long long LL; int T;
int n,c[];
int w[];
int mask[];
int dp[]; bool C(int x){
bool can[];
memset(can,,sizeof(can));
can[] = true;
int sum = ;
for( int i=;i<=n;i++ )if((x>>(i-))&){
sum += w[i];
for( int j=c[];j>=w[i];j--){
if( can[j-w[i]] ) can[j] = true;
}
}
for(int i=;i<=c[];i++){
if( can[i]&&sum-i<=c[] ) return true;
}
return false;
} int main(){
scanf("%d",&T);
for(int t=;t<=T;t++){
scanf("%d%d%d",&n,&c[],&c[]);
for(int i=;i<=n;i++) scanf("%d",&w[i]);
int ptr = ;
for(int i=;i<(<<n);i++){
if( C(i) ) mask[++ptr] = i;
}
const int INF = ;
fill(dp,dp+,INF);
dp[] = ;
for(int i=;i<=ptr;i++){
for(int j=(<<n)-;j>=;j--) if((mask[i]&j)==) {
dp[mask[i]|j] = min(dp[mask[i]|j],dp[j]+);
}
}
printf("Scenario #%d:\n%d\n\n",t,dp[(<<n)-]);
}
return ;
}

代码