您的位置:首页 > 其它

POJ2923:Relocation(状态压缩dp)

2017-07-16 09:10 183 查看
题目链接:点击打开链接

 题目大意:

          题目大概意思就是给n个物品,他们分别有自己的体积,和两辆车,每辆车每次可以拉v1,v2体积的货物。问最少多少次可以运完。

题意解析:

           做的第一道状压题,也算初步了解了状压的用法。大概就是用二进制的1和0代表当前状态此物品的有无。

           用状压预处理出所有可以一次被运走的状态,一次被运走不是大于两辆车总体积就可以,而是需要将这些物品体积能够成功的分到两辆车上,然后用这些状态对全部状态也就是全为1的状态进行01背包。注意东西不能重复运,可以用位运算符'&'判断。细节代码详解

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
#include <set>
using namespace std;
typedef long long LL;
int INF=1e9+7;
int dp[1050];
int sta[1050];
int w[1050];
int vis[1050];
int n,v1,v2;
bool check(int k)
{
memset(vis,0,sizeof(vis));
vis[0]=1;
int sum=0;
for(int i=0;i<=n;i++)
{
if((k>>i)&1)            //利用位运算依次判断物品的有无
{
sum=sum+w[i];
for(int j=v1;j>=w[i];j--)       //此处为一个打标记过程,假如第一次进的w[1],会将w[1]
if(vis[j-w[i]])             //打上标记,再进w[2],会将w[2]和w[1]+w[2]打上标记,总之
vis[j]=1;               //可以遍历所有的状态子集,具体可自行模拟
}
}
for(int j=0;j<=v1;j++)
{
if(vis[j]&&(sum-j)<=v2)             //利用vis数组判断是否该状态可以被成功分在两辆车上
return true;
}
return 0;
}
int main()
{
int QAQ,kase=0;
scanf("%d",&QAQ);
while(QAQ--)
{
scanf("%d%d%d",&n,&v1,&v2);
for(int i=0;i<n;i++)
scanf("%d",&w[i]);
int V=(1<<n)-1;                     //总体积为全1的集合
int g=0;
for(int i=1;i<(1<<n);i++)           //对所有状态进行遍历
{
dp[i]=INF;
if(check(i))                    //判断是否符合要求,符合就保存状态
sta[g++]=i;
}
dp[0]=0;
for(int i=0;i<g;i++)
{
for(int j=V;j>=0;j--)
{
if(!(j&sta[i]))             //判断当前是否有重复运的物品
dp[j|sta[i]]=min(dp[j|sta[i]],dp[j]+1);     //01背包因为减法不好用2进制表示,因此选择加法的DP
}
}
printf("Scenario #%d:\n",++kase);
printf("%d\n\n",dp[V]);
}
}


        

        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: